前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SAS-走近Log,实现程序的“风险控制”

SAS-走近Log,实现程序的“风险控制”

作者头像
Setup
发布2019-10-21 16:45:47
3K0
发布2019-10-21 16:45:47
举报

从第一天学习SAS开始,就摆脱不了看SAS日志,每次运行完程序的第一件事,不是看程序运行的结果,而是点击一下Log页面,第二件事也不是去看结果,而是仔细的浏览Log里面,有没有红色的字体出现(ERROR),有没有绿色的字体出现(WARNING),接着在看有没有黑色的_ERROR_=1出现(出现这种情况,一般是你的数据不满足条件或者语法有问题,下面会有例子),接着还得再看看有没有蓝色的字体(XXX未初始化)出现。就目前小编主要看的也就这四种常见的问题。肯定是不全的,各位大神如有兴趣可以留言补充~

1 概述日志作用

1、检查语法

日志最重要的一个作用就是在运行程序的时候,检查你的语法是否有问题。以目前小编常见的Log报出的错误分为几类。

ERROR

ERROR:在log中是以红色的字体存在,有ERROR存在的程序,是会的结果造成一定影响的,一般都是存在语法的问题。(仅举一例,如下截图),同时我们也可以通过故意error,让Log来提示我们语法..

WARNING

WARNING:在Log中是以绿色的字体存在,有WARNING程序也是有点问题,有时候不太影响结果,不过这个得具体情况具体看,最理想的情况下是零ERROR零WARNING,当有WARNING存在的时候,还是应该Check一下你的程序,如果解释的过去,到也无所谓..纯属个人早期观点,小编现在对自己有要求了,尽量时Log达到理想状态。(下图,随便造了一条WARNING)

_ERROR_

注意此处的_ERROR_非前面提到的ERROR,此处的_ERROR_是以黑色字体存在的,当出现这个的时候一定要注意一下程序,虽然程序结果不一定有问题,可能是数据或者其他的什么不满足你程序的要求,但是还是要在次check下程序。兴许真的是你程序写错了。顺便再一句相关的Option选项,在程序的开始可以加一个option error=1;的选项这样的话如果出现了黑色的error不会哗啦出现一大屏幕,没一类或者每一处error只在log上显示一次。这个是一个很好的option。(下面举个例子,将变量字符型转换成数值型(日期),采用input的方式,此次为数据问题,非程序问题,但是此处的黑色error是可以通过程序去避免产生的)。

蓝色的“NOTE”

看日志,除了上面有颜色的需要看,蓝色的也需要看,有一些蓝色的NOTE虽然不严重,但是还是需要以一个严谨的态度对待程序,哪些蓝色的需要注意呢,首当其冲当然是“XXX未初始化”、“函数xxx的参数无效”....针对未初始化的情况,可能提示你的变量名称写错了等等..是需要值得注意的,和消除未初始化在你Log中的存在。

期待您的补充

2、程序调试等提示

能通过上面的提示,我们就可以去找到你程序对应的位置,去修改程序,起到一个调试的作用,在如我们将一些信息Put到日志上去,辅助程序的编写,在如小编之前写过一篇SAS-Macro调试技巧的推送(可查看历史消息),里面就是利用一些options将Macro的执行可视化到日志上去。

3、当做证明

能check程序的语法,当然也能证明你程序语法没有问题(至于你程序的逻辑有无问题,这个只能靠你自己了),有时候你要证明你的程序没有问题,你说你没问题别人就信么,空口无凭啊,这个时候Log就派上用场了,那么问题来了。SAS中如何将Log导出?

2 俩种最常见方式导出Log

01 proc printto法

代码语言:javascript
复制
/*此处将log输出*/
proc printto log = "D:\日常练习\sas_checklog\test1.log" new; run;
/*恢复到SAS里面的日志*/
proc printto; run;

如上,将Log输出,然后又恢复到SAS系统里面的Log,这个方法呢,这个呢,需要你把你运行的程序放到他们中间。

02 dm replace法

代码语言:javascript
复制
dm 'log; log; file "D:\日常练习\sas_checklog\TEST1.log" replace ;'; run;

这个看起来很简单,俩种方式都各有优势,好像是前一种不会出现Log打印满的了情况,后一种则是先输出在SAS系统里面,然后在输出,在SAS里面Log行的是有限制的。当然一般正常的程序这个我们都用到上限的行。在补充一句,这个纯属个人对SAS的理解,未经证实,小编也懒得百度去证实一下真伪。如果是假的,记得留言告诉我一声哎~

03 书到用时方恨少

这个不是方法...是我要请教各位真神的一个问题,我想利用SAS语言(非手动方式点点点)将Log输出成pdf,并保留其Log中的颜色,请问可以做到么?该如何做到?我百度了良久,未找到解决方案,深感书到用时方恨少..

3 如何快捷的Check日志

导出日志不是我们最终的目标,导出了日志如何快速的找出你日志中存在的问题,最后形成一个report与日志与程序与结果一起保存下来,或者提交给你对接的人。才是我们最终的目的,这个应用一句从别人那里“借”来的话:“风险控制”,此处的风险指的是程序的风险。

如何快捷的从log中提取关键信息(error,warning等),并输出成报告,这个可以用Macro去实现,这个Macro怎么写呢,接下来就与我一起来写一下这个简单的Macro~

会用到的一些知识点

在贴代码前,私以为还是先简单提一下会用到的语法或者知识点,有兴趣的朋友可以着重找你想知道的~一个很简单Macro,这回被我写的感觉好复杂啊

1、正则表达式的运用

此处对正则的使用,是拆分输入的路径。如 E:\L\M\ yy.txt 此时可以得到E:\L\M

2、filename pipe的运用

此处是自动获取路径下的文件名及路径,关于pipe的使用,小编有一点需要提示:其原理是SAS与DOS的交互,因此你的路径夹路径中不能有空格,有空格就有点问题哎。暂时小编还不知道怎么解决此问题,因此小编的文件夹命名,都不在添加空格。

3、Check宏变量的几个函数的使用

此函数是检查你的宏变量(局部宏变量)是否定义。SAS中一个有3个函数Check 宏变量是否定义。如下(借用官网的几个实例)

4、call execute的使用

其功能就是执行语句,其实这里可以写很多的这种执行语句,不过小编还是用的不是很习惯,其实不能说是用的不习惯,因为用的过程中遇到了一点问题,暂时还没解决,因为觉得是execute语法的有点限制,不细说了,因为我也不太清楚,这个就涉及到data运行数据的原理了..。

5、定义Macro变量的方式

sql 与symput

写代码前,先看结果

首页:(第一个sheet的汇总)

后面的sheet是Log详情:小编将每个(ERROR,的前俩条与后俩条记录同时输出):

此处Macro 分为俩部分:%chk_log_ds:辅助性Macro,%chk_Log:功能性Macro

代码语言:javascript
复制
%macro chk_log_ds(ds,loop);
/*使用infile 导入数据*/
data &ds._1;
length type $100.;
infile fn&loop. end=last;
input desc $1-5000 @@;
line=_N_;
if index(desc,"_ERROR_")  then type="B_ERROR_";/*_ERROR_在数据操作中会有点问题,暂且在前面加一个字符后续会转化回去*/
else if index(desc,"ERROR") then type="ERROR";
else if index(desc,"WARNING") then type="WARNING";
else if index(desc,"未初始化") or index(desc,"uninitialized") then type="UNINITIALIZED";
run;
/*获取每个类型ERROR的前俩行与后俩行(便于详情列表中的review)*/
data log_tmp;
set &ds._1;
if ^missing(type) then do;
a1=line-2;
a2=line-1;
a3=line;
a4=line+1;
a5=line+2;end;
where ^missing(type);
keep LINE  type a1-a5;
run;
%let dsid=%sysfunc(open(log_tmp));
%let _nobs=%sysfunc(attrn(&dsid,nobs));
%let rc= %sysfunc(close(&dsid));
%if &_nobs. eq 0 %then %do;
data &ds._desc;
length LogName $200.;
LogName="&ds.";
ERROR=0;
_ERROR_=0;
WARNING=0;
UNINITIALIZED=0;
run; 
%end;
%if &_nobs. ne 0 %then %do;
proc freq data=LOG_tmp noprint; 
 table  type/   out=&ds._desc1(keep=type COUNT );
run;
proc sort data=&ds._desc1  out=&ds._desc1 ;by type ;quit;
proc transpose data=&ds._desc1 out=&ds._desc  ;
var COUNT;
ID type;
IDLABEL type;
run;
data &ds._desc;
set &ds._desc;
length LogName $200.;
LogName="&ds.";
DROP _NAME_ _LABEL_;
run;
proc sort data=log_tmp(drop= type)  out=log_tmp_  ;by LINE ;quit;
proc transpose data=log_tmp_ out=log_tmp_  prefix=ORRES;
by LINE;
var a1-a5;
run;
proc sql noprint;
select ORRES1 into:varlist separated by ' '  from log_tmp_   ;
quit;
data &ds.;
set &ds._1 ;
if type="B_ERROR_" then Type="_ERROR_";/*转化回本身*/
where line in (&varlist.);
run;
proc delete data=LOG_tmp LOG_tmp_  &ds._1 &ds._desc1 ;quit;
%end;
%mend;
代码语言:javascript
复制
%macro chk_log_mn(path=,encoding=gb2312);
/**********************************************************
check是单个log的核查,还是多个log的和核查
原理是:当path填写了具体的文件名称(以txt 或者 Log后缀的文件名称)
当path为一个文件路径时,自动扫描获取文件路径下的txt/Log文件。
并以log名称为数据集名称在给log取名时需要注意.
***********************************************************/
%put NOTE:&path.;
%let file=%sysfunc(PRXCHANGE(s/(.*)\\.*/\1/,-1,&path));
%put NOTE:&file.;
%if %upcase(%qscan(%qscan(&path,-1,'\'),-1,'.'))=TXT  or %upcase(%qscan(%qscan(&path,-1,'\'),-1,'.'))=LOG  %then %do;
%let _mian=%scan(&path,-1,'\');
%put NOTE:&_mian.;
%end;
/**********************************************************
利用Pipe或缺文件夹下文件列表
***********************************************************/
filename xcl_fil pipe "dir &file\*.*/b/s"; 
data _templog;
infile xcl_fil truncover;
input fname $char1000.;
put fname=;
dsn=scan(scan(fname,-1,'\'),1,'.');
/**********************************************************
用symlocal函数检查是否创建了_Main宏变量
如果创建则返回1,没有创建则返回0
/************************************************************/
%if %symlocal(_mian)=1 %then %do;
if find(upcase(fname),upcase("&_mian."))>0  then output;
%end;
%else %if %symlocal(_mian)=0 %then %do;
if find(upcase(fname),'.TXT')>0 or find(upcase(fname),'.LOG')>0   then output;
%end;
;
run;
data _templog2;
length data1 $20000.;
set _templog;
/**********************************************************
创建一个filename 利用filename
利用call execute 执行语句
利用复制性Macro实现对Log数据集的加工/筛选
***********************************************************/
call execute("filename "||strip(compress("fn"||_N_))||'  "'||strip(fname)||strip('"  encoding=')||left("&encoding.;"));
call symput('N'||compress(put(_n_,best.)),strip(dsn));
run;
%let dsid=%sysfunc(open(_templog2));
%let nobs=%sysfunc(attrn(&dsid,nobs));
%let rc= %sysfunc(close(&dsid));
%do j=1 %to &nobs;
%chk_log_ds(&&N&j.,&j.)
%end;
data contents;
retain LogName;
set %do i=1 %to &nobs.;%sysfunc(compress(&&N&i.._desc))  %end;;
if missing(B_ERROR_) then B_ERROR_=0;
if missing(ERROR) then ERROR=0;
if missing(WARNING) then WARNING=0;
if missing(UNINITIALIZED) then UNINITIALIZED=0;
run;
data _lasttemp;
set contents;
where sum(B_ERROR_,ERROR,WARNING,UNINITIALIZED)^=0;
call symput('M'||compress(put(_n_,best.)),strip(LogName));
run;
%let dsid=%sysfunc(open(_lasttemp));
%let _mloop=%sysfunc(attrn(&dsid,nobs));
%let rc= %sysfunc(close(&dsid));
/**********************************************************
输出过程
***********************************************************/;
ods path tpt.template(read)  sasuser.templat(read) sashelp.tmplmst(read);
ods listing close;
ods RESULTS off;
ods escapechar='^';
ods excel file="&file.\ Log report.xlsx" options(contents="no"  FROZEN_HEADERS="Yes" autofilter='all' ) style=tag_1;
ods excel options(embedded_titles='no' embedded_footnotes='no');
%let ps1=800;
%let ls1=256;
Options ps=&ps1 ls=&ls1  nodate nonumber nocenter;
OPTIONS FORMCHAR="|----|+|---+=|-/\<>*"; 
ods excel options(sheet_name="Contents_Index"  START_AT='D4') ;
proc report data=contents  headskip headline nowd  
style(header)={just=c asis=on font_weight=bold font_style=italic} ;
column ("Contents of Table" LogName ERROR B_ERROR_ WARNING UNINITIALIZED);
define LogName/ computed display style=[ just=left tagattr='text'  cellwidth=15% ] ;
define ERROR/ display "ERROR" style=[ just=left tagattr='text' cellwidth=15% ] ;
define B_ERROR_/  display "^__ERROR_"  style=[ just=left tagattr='text' cellwidth=15% ];
define WARNING/ display style=[ just=left tagattr='text' cellwidth=15%] ;
define UNINITIALIZED/ display style=[ just=left tagattr='text' cellwidth=15%] ;
compute LogName ;
if LogName ne '' and  sum(B_ERROR_,ERROR,WARNING,UNINITIALIZED) ne 0  then do;
urlstring = "#" ||strip(LogName)|| "!A1";
call define(_col_, 'URL', urlstring);
end;
endcomp;
run;
ods excel options(START_AT='A1' ) ;
%do mlop=1 %to &_mloop.;
ods excel options(sheet_name="&&M&mlop" ) ;
proc report data=&&M&mlop.   headskip headline nowd   contents="Contents_Index." ;
column ("日志详情" _ALL_);
define line /  display style=[ just=left tagattr='text'  cellwidth=4% ] ;
define type / computed display style=[ just=left tagattr='text'  cellwidth=15% ] ;
define desc /  display  "Description" style=[ just=left tagattr='text'  cellwidth=80% ] ;
compute type ;
if type  eq "ERROR"  then do;
call define(_ROW_,"style","style={  foreground=RED  }"); 
end;
if type  eq "_ERROR_"  then do;
call define(_ROW_,"style","style={  foreground=black }"); 
end;
if type  eq "WARNING"  then do;
call define(_ROW_,"style","style={  foreground=Green }"); 
end;
if type  eq "UNINITIALIZED"  then do;
call define(_ROW_,"style","style={  foreground=Blue  }"); 
end;
endcomp;
run;
%end;
proc delete data=work._lasttemp   _templog _templog2 ;quit;
ods excel close;
ods  listing;
%mend;
%chk_log_mn(path=D:\日常练习\sas_checklog\);
%chk_log_mn(path=D:\日常练习\sas_checklog\test.log);
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-01-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 SAS程序分享号号号 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 概述日志作用
    • 1、检查语法
      • 2、程序调试等提示
        • 3、当做证明
        • 2 俩种最常见方式导出Log
          • 01 proc printto法
            • 02 dm replace法
              • 03 书到用时方恨少
              • 3 如何快捷的Check日志
                • 会用到的一些知识点
                  • 写代码前,先看结果
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档