Macros for SAS Application Developers
https://github.com/sasjs/core
mv_getjoblog.sas
Go to the documentation of this file.
1 /**
2  @file
3  @brief Extract the log from a completed SAS Viya Job
4  @details Extracts log from a Viya job and writes it out to a fileref.
5 
6  To query the job, you need the URI. Sample code for achieving this
7  is provided below.
8 
9  ## Example
10 
11  %* First, compile the macros;
12  filename mc url
13  "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
14  %inc mc;
15 
16  %* Next, create a job (in this case, a web service);
17  filename ft15f001 temp;
18  parmcards4;
19  data ;
20  rand=ranuni(0)*1000;
21  do x=1 to rand;
22  y=rand*4;
23  output;
24  end;
25  run;
26  proc sort data=&syslast
27  by descending y;
28  run;
29  ;;;;
30  %mv_createwebservice(path=/Public/temp,name=demo)
31 
32  %* Execute it;
33  %mv_jobexecute(path=/Public/temp
34  ,name=demo
35  ,outds=work.info
36  )
37 
38  %* Wait for it to finish;
39  data work.info;
40  set work.info;
41  where method='GET' and rel='state';
42  run;
43  %mv_jobwaitfor(ALL,inds=work.info,outds=work.jobstates)
44 
45  %* and grab the uri;
46  data _null_;
47  set work.jobstates;
48  call symputx('uri',uri);
49  run;
50 
51  %* Finally, fetch the log;
52  %mv_getjoblog(uri=&uri,outref=mylog)
53 
54  This macro is used by the mv_jobwaitfor.sas macro, which is generally a more
55  convenient way to wait for the job to finish before fetching the log.
56 
57  If the remote session calls `endsas` then it is not possible to get the log
58  from the provided uri, and so the log from the parent session is fetched
59  instead. This happens for a 400 response, eg below:
60 
61  ErrorResponse[version=2,status=400,err=5113,id=,message=The session
62  requested is currently in a failed or stopped state.,detail=[path:
63  /compute/sessions/LONGURI-ses0006/jobs/LONGURI/log/content, traceId: 63
64  51aa617d01fd2b],remediation=Correct the errors in the session request,
65  and create a new session.,targetUri=<null>,errors=[],links=[]]
66 
67  @param [in] access_token_var= The global macro variable to contain the access
68  token
69  @param [in] mdebug= (0) Set to 1 to enable DEBUG messages
70  @param [in] grant_type= valid values:
71  @li password
72  @li authorization_code
73  @li detect - will check if access_token exists, if not will use sas_services
74  if a SASStudioV session else authorization_code. Default option.
75  @li sas_services - will use oauth_bearer=sas_services.
76  @param [in] uri= The uri of the running job for which to fetch the status,
77  in the format `/jobExecution/jobs/$UUID` (unquoted).
78  @param [out] outref= The output fileref to which to APPEND the log (is always
79  appended).
80 
81 
82  @version VIYA V.03.04
83  @author Allan Bowe, source: https://github.com/sasjs/core
84 
85  <h4> SAS Macros </h4>
86  @li mp_abort.sas
87  @li mf_getplatform.sas
88  @li mf_existfileref.sas
89  @li mf_getuniquefileref.sas
90  @li mf_getuniquelibref.sas
91 
92 **/
93 
94 %macro mv_getjoblog(uri=0,outref=0
95  ,access_token_var=ACCESS_TOKEN
96  ,grant_type=sas_services
97  ,mdebug=0
98  );
99 %local dbg libref1 libref2 loglocation fname1 fname2;
100 %if &mdebug=1 %then %do;
101  %put &sysmacroname entry vars:;
102  %put _local_;
103 %end;
104 %else %let dbg=*;
105 
106 %local oauth_bearer;
107 %if &grant_type=detect %then %do;
108  %if %symexist(&access_token_var) %then %let grant_type=authorization_code;
109  %else %let grant_type=sas_services;
110 %end;
111 %if &grant_type=sas_services %then %do;
112  %let oauth_bearer=oauth_bearer=sas_services;
113  %let &access_token_var=;
114 %end;
115 
116 %mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
117  and &grant_type ne sas_services
118  )
119  ,mac=&sysmacroname
120  ,msg=%str(Invalid value for grant_type: &grant_type)
121 )
122 
123 /* validation in datastep for better character safety */
124 %local errmsg errflg;
125 data _null_;
126  uri=symget('uri');
127  if length(uri)<12 then do;
128  call symputx('errflg',1);
129  call symputx('errmsg',"URI is invalid (too short) - '&uri'",'l');
130  end;
131  if scan(uri,-1)='state' or scan(uri,1) ne 'jobExecution' then do;
132  call symputx('errflg',1);
133  call symputx('errmsg',
134  "URI should be in format /jobExecution/jobs/$$$$UUID$$$$"
135  !!" but is actually like:"!!uri,'l');
136  end;
137 run;
138 
139 %mp_abort(iftrue=(&errflg=1)
140  ,mac=&sysmacroname
141  ,msg=%str(&errmsg)
142 )
143 
144 %mp_abort(iftrue=(&outref=0)
145  ,mac=&sysmacroname
146  ,msg=%str(Output fileref should be provided)
147 )
148 
149 %if %mf_existfileref(&outref) ne 1 %then %do;
150  filename &outref temp;
151 %end;
152 
153 options noquotelenmax;
154 %local base_uri; /* location of rest apis */
155 %let base_uri=%mf_getplatform(VIYARESTAPI);
156 
157 /* prepare request*/
158 %let fname1=%mf_getuniquefileref();
159 %let fname2=%mf_getuniquefileref();
160 proc http method='GET' out=&fname1 &oauth_bearer
161  url="&base_uri&uri";
162  headers
163  %if &grant_type=authorization_code %then %do;
164  "Authorization"="Bearer &&&access_token_var"
165  %end;
166  ;
167 run;
168 %if &mdebug=1 %then %do;
169  %put &sysmacroname: fetching log loc from &uri;
170  data _null_;infile &fname1;input;putlog _infile_;run;
171 %end;
172 %if &SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201 %then
173 %do;
174  data _null_;infile &fname1;input;putlog _infile_;run;
175  %mp_abort(mac=&sysmacroname
176  ,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
177  )
178 %end;
179 
180 %let libref1=%mf_getuniquelibref();
181 libname &libref1 JSON fileref=&fname1;
182 data _null_;
183  set &libref1..root;
184  call symputx('loglocation',loglocation,'l');
185 run;
186 
187 /* validate log path*/
188 %let errflg=1;
189 %let errmsg=No loglocation entry in &fname1 fileref;
190 data _null_;
191  uri=symget('loglocation');
192  if length(uri)<12 then do;
193  call symputx('errflg',1);
194  call symputx('errmsg',"URI is invalid (too short) - '&uri'",'l');
195  end;
196  else if (scan(uri,1,'/') ne 'compute' or scan(uri,2,'/') ne 'sessions')
197  and (scan(uri,1,'/') ne 'files' or scan(uri,2,'/') ne 'files')
198  then do;
199  call symputx('errflg',1);
200  call symputx('errmsg',
201  "URI should be in format /compute/sessions/$$$$UUID$$$$/jobs/$$$$UUID$$$$"
202  !!" or /files/files/$$$$UUID$$$$"
203  !!" but is actually like:"!!uri,'l');
204  end;
205  else do;
206  call symputx('errflg',0,'l');
207  call symputx('logloc',uri,'l');
208  end;
209 run;
210 
211 %mp_abort(iftrue=(%str(&errflg)=1)
212  ,mac=&sysmacroname
213  ,msg=%str(&errmsg)
214 )
215 
216 /* we have a log uri - now fetch the log */
217 %&dbg.put &sysmacroname: querying &base_uri&logloc/content;
218 proc http method='GET' out=&fname2 &oauth_bearer
219  url="&base_uri&logloc/content?limit=10000";
220  headers
221  %if &grant_type=authorization_code %then %do;
222  "Authorization"="Bearer &&&access_token_var"
223  %end;
224  ;
225 run;
226 
227 %if &mdebug=1 %then %do;
228  %put &sysmacroname: fetching log content from &base_uri&logloc/content;
229  data _null_;infile &fname2;input;putlog _infile_;run;
230 %end;
231 
232 %if &SYS_PROCHTTP_STATUS_CODE=400 %then %do;
233  /* fetch log from parent session */
234  %let logloc=%substr(&logloc,1,%index(&logloc,%str(/jobs/))-1);
235  %&dbg.put &sysmacroname: Now querying &base_uri&logloc/log/content;
236  proc http method='GET' out=&fname2 &oauth_bearer
237  url="&base_uri&logloc/log/content?limit=10000";
238  headers
239  %if &grant_type=authorization_code %then %do;
240  "Authorization"="Bearer &&&access_token_var"
241  %end;
242  ;
243  run;
244  %if &mdebug=1 %then %do;
245  %put &sysmacroname: fetching log content from &base_uri&logloc/log/content;
246  data _null_;infile &fname2;input;putlog _infile_;run;
247  %end;
248 %end;
249 
250 %if &SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201
251 %then %do;
252  %if &mdebug ne 1 %then %do; /* have already output above */
253  data _null_;infile &fname2;input;putlog _infile_;run;
254  %end;
255  %mp_abort(mac=&sysmacroname
256  ,msg=%str(logfetch: &SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
257  )
258 %end;
259 
260 %let libref2=%mf_getuniquelibref();
261 libname &libref2 JSON fileref=&fname2;
262 data _null_;
263  file &outref mod;
264  if _n_=1 then do;
265  put "/** SASJS Viya Job Log Extract start: &uri **/";
266  end;
267  set &libref2..items end=last;
268  %if &mdebug=1 %then %do;
269  putlog line;
270  %end;
271  put line;
272  if last then do;
273  put "/** SASJS Viya Job Log Extract end: &uri **/";
274  end;
275 run;
276 
277 %if &mdebug=0 %then %do;
278  filename &fname1 clear;
279  filename &fname2 clear;
280  libname &libref1 clear;
281  libname &libref2 clear;
282 %end;
283 %else %do;
284  %put &sysmacroname exit vars:;
285  %put _local_;
286 %end;
287 %mend mv_getjoblog;
288 
289 
290