从第一天学习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_checklogtest1.log" new; run;
/*恢复到SAS里面的日志*/
proc printto; run;
如上,将Log输出,然后又恢复到SAS系统里面的Log,这个方法呢,这个呢,需要你把你运行的程序放到他们中间。
02 dm replace法
代码语言:javascript复制dm 'log; log; file "D:日常练习sas_checklogTEST1.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:LM yy.txt 此时可以得到E:LM
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_checklogtest.log);