Production Ready 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 
13  filename mc url "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 
18  filename ft15f001 temp;
19  parmcards4;
20  data ;
21  rand=ranuni(0)*1000;
22  do x=1 to rand;
23  y=rand*4;
24  output;
25  end;
26  run;
27  proc sort data=&syslast
28  by descending y;
29  run;
30  ;;;;
31  %mv_createwebservice(path=/Public/temp,name=demo)
32 
33  Execute it:
34 
35  %mv_jobexecute(path=/Public/temp
36  ,name=demo
37  ,outds=work.info
38  )
39 
40  Wait for it to finish, and grab the uri:
41 
42  data _null_;
43  set work.info;
44  if method='GET' and rel='self';
45  call symputx('uri',uri);
46  run;
47 
48  Finally, fetch the log:
49 
50  %mv_getjoblog(uri=&uri,outref=mylog)
51 
52  This macro is used by the mv_jobwaitfor.sas macro, which is generally a more
53  convenient way to wait for the job to finish before fetching the log.
54 
55 
56  @param [in] access_token_var= The global macro variable to contain the access token
57  @param [in] mdebug= set to 1 to enable DEBUG messages
58  @param [in] grant_type= valid values:
59  @li password
60  @li authorization_code
61  @li detect - will check if access_token exists, if not will use sas_services if
62  a SASStudioV session else authorization_code. Default option.
63  @li sas_services - will use oauth_bearer=sas_services.
64  @param [in] uri= The uri of the running job for which to fetch the status,
65  in the format `/jobExecution/jobs/$UUID/state` (unquoted).
66  @param [out] outref= The output fileref to which to APPEND the log (is always
67  appended).
68 
69 
70  @version VIYA V.03.04
71  @author Allan Bowe, source: https://github.com/sasjs/core
72 
73  <h4> SAS Macros </h4>
74  @li mp_abort.sas
75  @li mf_getplatform.sas
76  @li mf_existfileref.sas
77  @li ml_json.sas
78 
79 **/
80 
81 %macro mv_getjoblog(uri=0,outref=0
82  ,contextName=SAS Job Execution compute context
83  ,access_token_var=ACCESS_TOKEN
84  ,grant_type=sas_services
85  ,mdebug=0
86  );
87 %local oauth_bearer;
88 %if &grant_type=detect %then %do;
89  %if %symexist(&access_token_var) %then %let grant_type=authorization_code;
90  %else %let grant_type=sas_services;
91 %end;
92 %if &grant_type=sas_services %then %do;
93  %let oauth_bearer=oauth_bearer=sas_services;
94  %let &access_token_var=;
95 %end;
96 
97 %mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
98  and &grant_type ne sas_services
99  )
100  ,mac=&sysmacroname
101  ,msg=%str(Invalid value for grant_type: &grant_type)
102 )
103 
104 /* validation in datastep for better character safety */
105 %local errmsg errflg;
106 data _null_;
107  uri=symget('uri');
108  if length(uri)<12 then do;
109  call symputx('errflg',1);
110  call symputx('errmsg',"URI is invalid (too short) - '&uri'",'l');
111  end;
112  if scan(uri,-1)='state' or scan(uri,1) ne 'jobExecution' then do;
113  call symputx('errflg',1);
114  call symputx('errmsg',
115  "URI should be in format /jobExecution/jobs/$$$$UUID$$$$"
116  !!" but is actually like: &uri",'l');
117  end;
118 run;
119 
120 %mp_abort(iftrue=(&errflg=1)
121  ,mac=&sysmacroname
122  ,msg=%str(&errmsg)
123 )
124 
125 %mp_abort(iftrue=(&outref=0)
126  ,mac=&sysmacroname
127  ,msg=%str(Output fileref should be provided)
128 )
129 
130 %if %mf_existfileref(&outref) ne 1 %then %do;
131  filename &outref temp;
132 %end;
133 
134 options noquotelenmax;
135 %local base_uri; /* location of rest apis */
136 %let base_uri=%mf_getplatform(VIYARESTAPI);
137 
138 /* prepare request*/
139 %local fname1;
140 %let fname1=%mf_getuniquefileref();
141 proc http method='GET' out=&fname1 &oauth_bearer
142  url="&base_uri&uri";
143  headers
144  %if &grant_type=authorization_code %then %do;
145  "Authorization"="Bearer &&&access_token_var"
146  %end;
147  ;
148 run;
149 %if &SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201 %then
150 %do;
151  data _null_;infile &fname1;input;putlog _infile_;run;
152  %mp_abort(mac=&sysmacroname
153  ,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
154  )
155 %end;
156 %local fname2 fname3 fpath1 fpath2 fpath3;
157 %let fname2=%mf_getuniquefileref();
158 %let fname3=%mf_getuniquefileref();
159 %let fpath1=%sysfunc(pathname(&fname1));
160 %let fpath2=%sysfunc(pathname(&fname2));
161 %let fpath3=%sysfunc(pathname(&fname3));
162 
163 /* compile the lua JSON module */
164 %ml_json()
165 /* read using LUA - this allows the code to be of any length */
166 data _null_;
167  file "&fpath3..lua";
168  put '
169  infile = io.open (sas.symget("fpath1"), "r")
170  outfile = io.open (sas.symget("fpath2"), "w")
171  io.input(infile)
172  local resp=json.decode(io.read())
173  local logloc=resp["logLocation"]
174  outfile:write(logloc)
175  io.close(infile)
176  io.close(outfile)
177  ';
178 run;
179 %inc "&fpath3..lua";
180 /* get log path*/
181 %let errflg=1;
182 %let errmsg=No entry in &fname2 fileref;
183 data _null_;
184  infile &fname2;
185  input;
186  uri=_infile_;
187  if length(uri)<12 then do;
188  call symputx('errflg',1);
189  call symputx('errmsg',"URI is invalid (too short) - '&uri'",'l');
190  end;
191  if scan(uri,1) ne 'files' or scan(uri,2) ne 'files' then do;
192  call symputx('errflg',1);
193  call symputx('errmsg',
194  "URI should be in format /files/files/$$$$UUID$$$$"
195  !!" but is actually like: &uri",'l');
196  end;
197  call symputx('errflg',0,'l');
198  call symputx('logloc',uri,'l');
199 run;
200 
201 %mp_abort(iftrue=(&errflg=1)
202  ,mac=&sysmacroname
203  ,msg=%str(&errmsg)
204 )
205 
206 /* we have a log uri - now fetch the log */
207 proc http method='GET' out=&fname1 &oauth_bearer
208  url="&base_uri&logloc/content";
209  headers
210  %if &grant_type=authorization_code %then %do;
211  "Authorization"="Bearer &&&access_token_var"
212  %end;
213  ;
214 run;
215 %if &SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201 %then
216 %do;
217  data _null_;infile &fname1;input;putlog _infile_;run;
218  %mp_abort(mac=&sysmacroname
219  ,msg=%str(logfetch: &SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
220  )
221 %end;
222 
223 data _null_;
224  file "&fpath3..lua";
225  put '
226  infile = io.open (sas.symget("fpath1"), "r")
227  outfile = io.open (sas.symget("fpath2"), "w")
228  io.input(infile)
229  local resp=json.decode(io.read())
230  for i, v in pairs(resp["items"]) do
231  outfile:write(v.line,"\n")
232  end
233  io.close(infile)
234  io.close(outfile)
235  ';
236 run;
237 %inc "&fpath3..lua";
238 
239 /* write log out to the specified fileref */
240 data _null_;
241  infile &fname2 end=last;
242  file &outref mod;
243  if _n_=1 then do;
244  put "/** SASJS Viya Job Log Extract start: &uri **/";
245  end;
246  input;
247  put _infile_;
248  %if &mdebug=1 %then %do;
249  putlog _infile_;
250  %end;
251  if last then do;
252  put "/** SASJS Viya Job Log Extract end: &uri **/";
253  end;
254 run;
255 
256 %if &mdebug=0 %then %do;
257  filename &fname1 clear;
258  filename &fname2 clear;
259  filename &fname3 clear;
260 %end;
261 %else %do;
262  %put _local_;
263 %end;
264 %mend;
265 
266 
267