diff --git a/all.sas b/all.sas index 40d9f7b..9c3b333 100644 --- a/all.sas +++ b/all.sas @@ -1754,9 +1754,15 @@ Usage: rc=stpsrvset('program error', 0); call symputx("syscc",0,"g"); run; + endsas; %end; - - %if "%substr(&sysvlong.xxxxxxx,1,9)" ne "9.04.01M3" %then %do; + %else %if "&sysprocessmode " = "SAS Compute Server " %then %do; + /* endsas kills the session making it harder to fetch results */ + data _null_; + abort; + run; + %end; + %else %if "%substr(&sysvlong.xxxxxxxxx,1,9)" ne "9.04.01M3" %then %do; %put NOTE: Ending SAS session due to:; %put NOTE- &msg; endsas; @@ -15064,21 +15070,19 @@ run; /** @file @brief Extract the log from a completed SAS Viya Job - @details Extracts log from a Viya job and writes it out to a fileref + @details Extracts log from a Viya job and writes it out to a fileref. To query the job, you need the URI. Sample code for achieving this is provided below. ## Example - First, compile the macros: - + %* First, compile the macros; filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas"; %inc mc; - Next, create a job (in this case, a web service): - + %* Next, create a job (in this case, a web service); filename ft15f001 temp; parmcards4; data ; @@ -15094,28 +15098,40 @@ run; ;;;; %mv_createwebservice(path=/Public/temp,name=demo) - Execute it: - + %* Execute it; %mv_jobexecute(path=/Public/temp ,name=demo ,outds=work.info ) - Wait for it to finish, and grab the uri: - - data _null_; + %* Wait for it to finish; + data work.info; set work.info; - if method='GET' and rel='self'; + where method='GET' and rel='state'; + run; + %mv_jobwaitfor(ALL,inds=work.info,outds=work.jobstates) + + %* and grab the uri; + data _null_; + set work.jobstates; call symputx('uri',uri); run; - Finally, fetch the log: - + %* Finally, fetch the log; %mv_getjoblog(uri=&uri,outref=mylog) This macro is used by the mv_jobwaitfor.sas macro, which is generally a more convenient way to wait for the job to finish before fetching the log. + If the remote session calls `endsas` then it is not possible to get the log + from the provided uri, and so the log from the parent session is fetched + instead. This happens for a 400 response, eg below: + + ErrorResponse[version=2,status=400,err=5113,id=,message=The session + requested is currently in a failed or stopped state.,detail=[path: + /compute/sessions/LONGURI-ses0006/jobs/LONGURI/log/content, traceId: 63 + 51aa617d01fd2b],remediation=Correct the errors in the session request, + and create a new session.,targetUri=,errors=[],links=[]] @param [in] access_token_var= The global macro variable to contain the access token @@ -15127,7 +15143,7 @@ run; if a SASStudioV session else authorization_code. Default option. @li sas_services - will use oauth_bearer=sas_services. @param [in] uri= The uri of the running job for which to fetch the status, - in the format `/jobExecution/jobs/$UUID/state` (unquoted). + in the format `/jobExecution/jobs/$UUID` (unquoted). @param [out] outref= The output fileref to which to APPEND the log (is always appended). @@ -15185,7 +15201,7 @@ data _null_; call symputx('errflg',1); call symputx('errmsg', "URI should be in format /jobExecution/jobs/$$$$UUID$$$$" - !!" but is actually like: &uri",'l'); + !!" but is actually like:"!!uri,'l'); end; run; @@ -15218,6 +15234,10 @@ proc http method='GET' out=&fname1 &oauth_bearer %end; ; run; +%if &mdebug=1 %then %do; + %put &sysmacroname: fetching log loc from &uri; + data _null_;infile &fname1;input;putlog _infile_;run; +%end; %if &SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201 %then %do; data _null_;infile &fname1;input;putlog _infile_;run; @@ -15255,43 +15275,69 @@ run; data _null_; infile &fname2; input; - uri=_infile_; + uri=cats(_infile_); if length(uri)<12 then do; call symputx('errflg',1); call symputx('errmsg',"URI is invalid (too short) - '&uri'",'l'); end; - if scan(uri,1) ne 'files' or scan(uri,2) ne 'files' then do; + else if (scan(uri,1,'/') ne 'compute' or scan(uri,2,'/') ne 'sessions') + and (scan(uri,1,'/') ne 'files' or scan(uri,2,'/') ne 'files') + then do; call symputx('errflg',1); call symputx('errmsg', - "URI should be in format /files/files/$$$$UUID$$$$" - !!" but is actually like: &uri",'l'); + "URI should be in format /compute/sessions/$$$$UUID$$$$/jobs/$$$$UUID$$$$" + !!" or /files/files/$$$$UUID$$$$" + !!" but is actually like:"!!uri,'l'); + end; + else do; + call symputx('errflg',0,'l'); + call symputx('logloc',uri,'l'); end; - call symputx('errflg',0,'l'); - call symputx('logloc',uri,'l'); run; -%mp_abort(iftrue=(&errflg=1) +%mp_abort(iftrue=(%str(&errflg)=1) ,mac=&sysmacroname ,msg=%str(&errmsg) ) /* we have a log uri - now fetch the log */ +%&dbg.put &sysmacroname: querying &base_uri&logloc/content; proc http method='GET' out=&fname1 &oauth_bearer - url="&base_uri&logloc/content"; + url="&base_uri&logloc/content?limit=10000"; headers %if &grant_type=authorization_code %then %do; "Authorization"="Bearer &&&access_token_var" %end; ; run; -%if &SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201 %then -%do; +%if &mdebug=1 %then %do; + %put &sysmacroname: fetching log content from &base_uri&logloc/content; + data _null_;infile &fname1;input;putlog _infile_;run; +%end; +%if &SYS_PROCHTTP_STATUS_CODE=400 %then %do; + /* fetch log from parent session */ + %let logloc=%substr(&logloc,1,%index(&logloc,%str(/jobs/))-1); + %&dbg.put &sysmacroname: Now querying &base_uri&logloc/log/content; + proc http method='GET' out=&fname1 &oauth_bearer + url="&base_uri&logloc/log/content?limit=10000"; + headers + %if &grant_type=authorization_code %then %do; + "Authorization"="Bearer &&&access_token_var" + %end; + ; + run; + %if &mdebug=1 %then %do; + %put &sysmacroname: fetching log content from &base_uri&logloc/log/content; + data _null_;infile &fname1;input;putlog _infile_;run; + %end; +%end; +%if &SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201 +%then %do; data _null_;infile &fname1;input;putlog _infile_;run; %mp_abort(mac=&sysmacroname ,msg=%str(logfetch: &SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE) ) %end; - data _null_; file "&fpath3..lua"; put ' diff --git a/base/mp_abort.sas b/base/mp_abort.sas index b7d3c44..4c5ea00 100644 --- a/base/mp_abort.sas +++ b/base/mp_abort.sas @@ -134,9 +134,15 @@ rc=stpsrvset('program error', 0); call symputx("syscc",0,"g"); run; + endsas; %end; - - %if "%substr(&sysvlong.xxxxxxx,1,9)" ne "9.04.01M3" %then %do; + %else %if "&sysprocessmode " = "SAS Compute Server " %then %do; + /* endsas kills the session making it harder to fetch results */ + data _null_; + abort; + run; + %end; + %else %if "%substr(&sysvlong.xxxxxxxxx,1,9)" ne "9.04.01M3" %then %do; %put NOTE: Ending SAS session due to:; %put NOTE- &msg; endsas; diff --git a/tests/base/mp_abort.test.1.sas b/tests/base/mp_abort.test.1.sas index cbff2ce..85d9bed 100644 --- a/tests/base/mp_abort.test.1.sas +++ b/tests/base/mp_abort.test.1.sas @@ -10,6 +10,7 @@

SAS Macros

@li mp_abort.sas + @li mp_assert.sas **/ diff --git a/tests/viya/mv_getjoblog.test.sas b/tests/viya/mv_getjoblog.test.sas new file mode 100644 index 0000000..499a2c2 --- /dev/null +++ b/tests/viya/mv_getjoblog.test.sas @@ -0,0 +1,64 @@ +/** + @file + @brief Testing mv_createwebservice macro + +

SAS Macros

+ @li mp_assert.sas + @li mv_createjob.sas + @li mv_jobexecute.sas + @li mv_jobwaitfor.sas + @li mv_getjoblog.sas + +**/ + +/** + * Test Case 1 + */ + +/* create a service */ +filename testref temp; +data _null_; + file testref; + put 'endsas;'; +run; +%mv_createjob( + path=&mcTestAppLoc/jobs/temp, + code=testref, + name=testjob +) + +%* Execute it; +%mv_jobexecute( + path=&mcTestAppLoc/jobs/temp, + name=testjob, + outds=work.info, +) + +%* Wait for it to finish; +data work.info; + set work.info; + where method='GET' and rel='state'; +run; +%mv_jobwaitfor(ALL,inds=work.info,outds=work.jobstates) + +%* and grab the uri; +data _null_; + set work.jobstates; + call symputx('uri',uri); +run; + +%* Finally, fetch the log; +%mv_getjoblog(uri=%str(&uri),outref=mylog) + + +data _null_; + infile mylog; + input; + if index(_infile_,'endsas;') then call symputx('found',1); + else call symputx('found',0); +run; + +%mp_assert( + iftrue=(%str(&found)=1), + desc=Check if the log was still fetched even though endsas was submitted +) \ No newline at end of file diff --git a/viya/mv_getjoblog.sas b/viya/mv_getjoblog.sas index 7c4ce0a..dc44c9b 100644 --- a/viya/mv_getjoblog.sas +++ b/viya/mv_getjoblog.sas @@ -1,21 +1,19 @@ /** @file @brief Extract the log from a completed SAS Viya Job - @details Extracts log from a Viya job and writes it out to a fileref + @details Extracts log from a Viya job and writes it out to a fileref. To query the job, you need the URI. Sample code for achieving this is provided below. ## Example - First, compile the macros: - + %* First, compile the macros; filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas"; %inc mc; - Next, create a job (in this case, a web service): - + %* Next, create a job (in this case, a web service); filename ft15f001 temp; parmcards4; data ; @@ -31,28 +29,40 @@ ;;;; %mv_createwebservice(path=/Public/temp,name=demo) - Execute it: - + %* Execute it; %mv_jobexecute(path=/Public/temp ,name=demo ,outds=work.info ) - Wait for it to finish, and grab the uri: - - data _null_; + %* Wait for it to finish; + data work.info; set work.info; - if method='GET' and rel='self'; + where method='GET' and rel='state'; + run; + %mv_jobwaitfor(ALL,inds=work.info,outds=work.jobstates) + + %* and grab the uri; + data _null_; + set work.jobstates; call symputx('uri',uri); run; - Finally, fetch the log: - + %* Finally, fetch the log; %mv_getjoblog(uri=&uri,outref=mylog) This macro is used by the mv_jobwaitfor.sas macro, which is generally a more convenient way to wait for the job to finish before fetching the log. + If the remote session calls `endsas` then it is not possible to get the log + from the provided uri, and so the log from the parent session is fetched + instead. This happens for a 400 response, eg below: + + ErrorResponse[version=2,status=400,err=5113,id=,message=The session + requested is currently in a failed or stopped state.,detail=[path: + /compute/sessions/LONGURI-ses0006/jobs/LONGURI/log/content, traceId: 63 + 51aa617d01fd2b],remediation=Correct the errors in the session request, + and create a new session.,targetUri=,errors=[],links=[]] @param [in] access_token_var= The global macro variable to contain the access token @@ -64,7 +74,7 @@ if a SASStudioV session else authorization_code. Default option. @li sas_services - will use oauth_bearer=sas_services. @param [in] uri= The uri of the running job for which to fetch the status, - in the format `/jobExecution/jobs/$UUID/state` (unquoted). + in the format `/jobExecution/jobs/$UUID` (unquoted). @param [out] outref= The output fileref to which to APPEND the log (is always appended). @@ -122,7 +132,7 @@ data _null_; call symputx('errflg',1); call symputx('errmsg', "URI should be in format /jobExecution/jobs/$$$$UUID$$$$" - !!" but is actually like: &uri",'l'); + !!" but is actually like:"!!uri,'l'); end; run; @@ -155,6 +165,10 @@ proc http method='GET' out=&fname1 &oauth_bearer %end; ; run; +%if &mdebug=1 %then %do; + %put &sysmacroname: fetching log loc from &uri; + data _null_;infile &fname1;input;putlog _infile_;run; +%end; %if &SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201 %then %do; data _null_;infile &fname1;input;putlog _infile_;run; @@ -192,43 +206,69 @@ run; data _null_; infile &fname2; input; - uri=_infile_; + uri=cats(_infile_); if length(uri)<12 then do; call symputx('errflg',1); call symputx('errmsg',"URI is invalid (too short) - '&uri'",'l'); end; - if scan(uri,1) ne 'files' or scan(uri,2) ne 'files' then do; + else if (scan(uri,1,'/') ne 'compute' or scan(uri,2,'/') ne 'sessions') + and (scan(uri,1,'/') ne 'files' or scan(uri,2,'/') ne 'files') + then do; call symputx('errflg',1); call symputx('errmsg', - "URI should be in format /files/files/$$$$UUID$$$$" - !!" but is actually like: &uri",'l'); + "URI should be in format /compute/sessions/$$$$UUID$$$$/jobs/$$$$UUID$$$$" + !!" or /files/files/$$$$UUID$$$$" + !!" but is actually like:"!!uri,'l'); + end; + else do; + call symputx('errflg',0,'l'); + call symputx('logloc',uri,'l'); end; - call symputx('errflg',0,'l'); - call symputx('logloc',uri,'l'); run; -%mp_abort(iftrue=(&errflg=1) +%mp_abort(iftrue=(%str(&errflg)=1) ,mac=&sysmacroname ,msg=%str(&errmsg) ) /* we have a log uri - now fetch the log */ +%&dbg.put &sysmacroname: querying &base_uri&logloc/content; proc http method='GET' out=&fname1 &oauth_bearer - url="&base_uri&logloc/content"; + url="&base_uri&logloc/content?limit=10000"; headers %if &grant_type=authorization_code %then %do; "Authorization"="Bearer &&&access_token_var" %end; ; run; -%if &SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201 %then -%do; +%if &mdebug=1 %then %do; + %put &sysmacroname: fetching log content from &base_uri&logloc/content; + data _null_;infile &fname1;input;putlog _infile_;run; +%end; +%if &SYS_PROCHTTP_STATUS_CODE=400 %then %do; + /* fetch log from parent session */ + %let logloc=%substr(&logloc,1,%index(&logloc,%str(/jobs/))-1); + %&dbg.put &sysmacroname: Now querying &base_uri&logloc/log/content; + proc http method='GET' out=&fname1 &oauth_bearer + url="&base_uri&logloc/log/content?limit=10000"; + headers + %if &grant_type=authorization_code %then %do; + "Authorization"="Bearer &&&access_token_var" + %end; + ; + run; + %if &mdebug=1 %then %do; + %put &sysmacroname: fetching log content from &base_uri&logloc/log/content; + data _null_;infile &fname1;input;putlog _infile_;run; + %end; +%end; +%if &SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201 +%then %do; data _null_;infile &fname1;input;putlog _infile_;run; %mp_abort(mac=&sysmacroname ,msg=%str(logfetch: &SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE) ) %end; - data _null_; file "&fpath3..lua"; put '