Buscar

Limpiar filtro
Artículo
Pierre-Yves Duquesnoy · 3 mayo, 2022

Depuración tradicional en ObjectScript

Encontrar errores en tu código o examinar un comportamiento inesperado es el principal objetivo de la depuración. Trataré de actualizar las herramientas tradicionales aparte de las ayudas que tienen Studio, VScode, Serenji... Las herramientas básicas que han estado ahí antes de que tu EDI preferido lo utilizara en segundo plano. Observamos 3 situaciones habituales: Depuración desde la línea de comandos en primer plano desde un terminal o una sesión similar. Si es posible, el antiguo comando BREAK (también con una postcondición) Te permite detenerte y examinar la situación cuando lo necesites. Es la opción más antigua, que se remonta al periodo anterior a ObjectScript. Más nuevo, pero similar, el comando ZBREAK, introduce puntos de control, además de simples Puntos de interrupción y un sofisticado conjunto de condiciones. ​​​ Aquí puedes consultar más información sobre la Depuración desde la línea de comandos Depuración en segundo plano Todo lo que se describe aquí es, por supuesto, igualmente útil y está disponible para la depuración desde la línea de comandos Primero, tienes la opción de escribir información adicional en un Global (temporal). Esto es útil si ya tienes una sospecha bastante clara de lo que hay que revisar Después, puedes volcar sus variables reales también a SPOOL o utilizar una salida redirigida o llamar a LOG^%ETN() para volcar toda la partición en ^ERRORS Global y examinarlo con SMP/System Operation/Application error log Como forma de mover el código de background al primer plano en la línea de comandos, puedes utilizar la depuración desde el intérprete de comandos o Shell. Esto se aplica normalmente a SQL y CSP/ZEN Depuración desde el intérprete de comandos Empiezo con una página CSP bastante simple como un ejemplo que contiene este código: . . . </head> <script language="Cache" method="init" arguments="file,.tName" returntype="%Integer"> BREAK ;; <<<< for debugging if 'file { set tName="*** no file ***",index="" } elseif $D(^ImportFile(file,-1)) { set tName=$O(^ImportFile(file,-1,"Class",""),1,index) } Else { set tName=$g(^ImportFile(file)),index=0 } quit index </script> <script language="Cache" method= Ahora ejecuto esto desde mi CSPshell y puedo ver y acceder a todas las variables y objetos IRISAPP> IRISAPP>do $system.CSP.Shell() CSP Shell CSP:IRISAPP>>> GET /csp/irisapp/Details.csp HTTP/1.1 200 OK Content-Type: text/html; charset=utf-8 Set-Cookie: CSPSESSIONID-UP-csp-irisapp-=15340 15340; path=/csp/irisapp/; httpOnly; sameSite=strict; Cache-Control: no-cache Date: Tue, 23 Nov 2021 19:03:29 GMT Expires: Thu, 29 Oct 1998 17:04:19 GMT Pragma: no-cache <html> <head> <!-- Put your page Title here --> <title> Details </title> <script language="javascript"> window.onmessage = function(event){ }; </script> <!-- function postDebug(item, index) { var escape = item.replace(/\//g, '-'); // fix slashes in dates //var escape = encodeURI(escape); var escape = encodeURIComponent(escape); // fix % and more if (item == '') escape = 'NULL'; //document.getElementById("div_debug").innerHTML += index + "," + escape + "<br>"; var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { //document.getElementById("div_debug").innerHTML += this.responseText; } if (this.readyState == 4 && this.status != 200) { //document.getElementById("div_debug").innerHTML += this.responseText; } }; //xhttp.open("GET", "/restAll/debug", true, "_SYSTEM", "SYS"); //xhttp.open("POST", "/restAll/click/" + escape + '/' + index, true, "_SYSTEM", "abc123"); xhttp.open("POST", "/restAll/click/" + escape + '/' + index, true, "_SYSTEM", "SYS"); ;xhttp.send(); } </script --> </head> <body> <table> <BREAK>zinit+1^csp.details.1 ;<<<<<<<<<<<<<<<<<<<<<<<<<<< IRISAPP 15e1>zwrite %CSPsc=1 %SYSLOG=1 %request=<OBJECT REFERENCE>[4@%CSP.Request] %response=<OBJECT REFERENCE>[1@%CSP.Response] %session=<OBJECT REFERENCE>[2@%CSP.Session] file=0 tDEBUG=3 tData="class,dc.data.rcc.GMbadge" tFile1=0 tFile2=0 tWhat="class" IRISAPP 15e1>zwrite %request %request=4@%CSP.Request ; <OREF> +----------------- general information --------------- | oref value: 4 | class name: %CSP.Request | reference count: 5 +----------------- attribute values ------------------ | AppData = $lb("",64,1,"","/csp/irisapp/","",1,"","",0,1,"","","/csp/irisapp","IRISAPP","","c:\intersystems\iris\csp\irisapp\",1,"","",1,"",900,2,2,"",3600,0,1,1,"",1,"","",1,1,0,2,2) <Set> | AppMatch = "/csp/irisapp/" <Set> | Application = "/csp/irisapp/" <Set> | CSPGatewayRequest = 0 <Set> | CharSet = "" <Set> | Class = "csp.details" <Set> | ContentType = "" <Set> | (ConvertCharSet) = "UTF8" | GatewayApplication = "" <Set> | GatewayBuild = "" <Set> |GatewayConnectionName = "" <Set> | GatewayError = "" | GatewayFunctions = "" <Set> |GatewayInstanceName = "" <Set> | GatewayNewId = 0 |GatewaySessionCookie = "" <Set> |GatewaySessionIdSource = "" <Set> | GatewayTimeout = 60 | Method = "GET" | NoResetTimeout = 0 | PageName = "Details.csp" |ProcessedRequestType = 1 | Protocol = "HTTP/1.1" <Set> | RegistryMethods = 1 | RequestId = "" | Secure = 0 <Set> | Service = "CSP" <Set> | URL = "/csp/irisapp/Details.csp" | URLPrefix = "" | UserAgent = "" <Set> +----------------- swizzled references --------------- | i%Content = "" | r%Content = "" +----------------------------------------------------- IRISAPP 15e1>zwrite %session %session=2@%CSP.Session ; <OREF> +----------------- general information --------------- | oref value: 2 | class name: %CSP.Session | %%OID: $lb("15340","%CSP.Session") | reference count: 5 +----------------- attribute values ------------------ | %Concurrency = 1 <Set> | AppTimeout = 900 | Application = "/csp/irisapp/" <Set> |ApplicationLicenses = "" | BrowserName = "" <Get> | BrowserPlatform = "" <Get> | BrowserVersion = "" <Get> | ByIdGroups = "" | CSPSessionCookie = "15340 15340" | CookiePath = "/csp/irisapp/" | CreateTime = "2021-11-23 18:56:54" <Set> | Debug = 0 <Set> | EndSession = 0 <Set> | ErrorPage = "" |(EventClassContext) = "" | GetNewId = 0 | GroupId = "" | HttpAuthorization = "" | KeepAlive = 1 | Key = "3"_$c(3)_"±Ð~"_$c(129)_"­t¼Ø´0"_$c(0)_"òÆÆùXNX"_$c(158)_"Å="_$c(152,1)_"T*"_$c(10)_"®«®"_$c(21) <Set> | Language = "de" | LastModified = "2021-11-23 18:58:28" <Set> | LicenseId = "_SYSTEM" <Set> | LogoutCleanup = 0 | MessageNumber = 1 <Set> | Namespace = "IRISAPP" | NewSession = 0 <Set> | (NoLicense) = 0 | OldTimeout = 5708603608 <Set> | PersistentHeaders = "" <Set> | Preserve = 0 <Set> | ProcessId = "" | Referrer = "" <Set> | RunNamespace = "" <Get,Set> | SOAPRequestCount = 0 |SecureSessionCookie = 0 | SecurityContext = $lb("_SYSTEM","%All,%DB_IRISLIB,%DB_IRISSYS","%All,%DB_IRISLIB,%DB_IRISSYS",32,-559038737) <Set> | SessionId = 15340 | SessionScope = 2 | (StickyLogin) = $lb("/csp/irisapp/","_SYSTEM",$lb("_SYSTEM","%All,%DB_IRISLIB,%DB_IRISSYS","%All,%DB_IRISLIB,%DB_IRISSYS",32,-559038737),"",0,0,0,1) <Get,Set> | UseSessionCookie = 2 | UserAgent = "" | UserCookieScope = 2 | nosave = 0 +--------------- calculated references --------------- | EventClass <Get,Set> | Username <Get> +----------------------------------------------------- IRISAPP 15e1>zwrite %response %response=1@%CSP.Response ; <OREF> +----------------- general information --------------- | oref value: 1 | class name: %CSP.Response | reference count: 5 +----------------- attribute values ------------------ | AllowOutputFlush = 0 |AvoidPartitionCleanup = 0 | (CSPGatewayData) = "" <Set> | CharSet = "utf-8" | ContentLength = "" | ContentType = "text/html" | CookiePath = "/csp/irisapp/" | Domain = "" | GzipOutput = "" <Set> | HTTPVersion = "" | HeaderCharSet = "" |Headers("CACHE-CONTROL") = "no-cache" | Headers("DATE") = "Tue, 23 Nov 2021 19:03:29 GMT" | Headers("EXPIRES") = "Thu, 29 Oct 1998 17:04:19 GMT" | Headers("PRAGMA") = "no-cache" | IgnoreRESTOutput = "" | InProgress = 1 <Set> | Language = "de" <Set> | NoCharSetConvert = 0 | OutputSessionToken = 1 | Redirect = "" | ServerSideRedirect = "" | Status = "200 OK" | Timeout = "" <Set> | TraceDump = 0 | UseASPredirect = 0 <Set> | UseHttpOnly = 1 | VaryByParam = "" +--------------- calculated references --------------- | Expires <Get,Set> +----------------------------------------------------- IRISAPP 15e1>g <BREAK>zinit+1^csp.details.1 IRISAPP 15e1>g <!-- tr><th colspan="3">#(tName)#</th></tr --> <tr><th>Line</th><th>*** no file ***</th><th>Line</th><th>*** no file ***</th></tr> </table> </body> </html> El siguiente ejemplo se refiere a SQL y utilizo esta propiedad calculada que falla de vez en cuando Property Random As %Numeric(SCALE = 2) [ Calculated, SqlComputed, SqlComputeCode = { Set rcc=$Random(5) BREAK Set {*} = $Random(10)/rcc} ]; ---------------------------------------------------------------------------- SQLquery= "select top 10 id,name,random from Sample.Person" ---------------------------------------------------------------------------- throws [SQLCODE: <-350> . . . ] [%msg: <Unexpected error executing SqlCompute code for field 'Random': <DIVIDE>%0AmBk1+5^%sqlcq.DEMO.cls33.1>]</pre></li> es obvio que la depuración de %0AmBk1+5^%sqlcq.DEMO.cls33.1 está fuera de nuestro alcance Para la depuración yo utilizo el SQLshell para aprovechar mi BREAK para ver todas las variables y objetos DEMO> DEMO>do $system.SQL.Shell() SQL Command Line Shell ---------------------------------------------------- The command prefix is currently set to: <<nothing>>. Enter <command>, 'q' to quit, '?' for help. [SQL]DEMO>>select top 10 id,name,random from Sample.Person 2. select top 10 id,name,random from Sample.Person ID Name Random . try { Set rcc=$Random(5) BREAK Set i%RandomO1 = $Random(10)/rcc ^ <BREAK>%0AmBk1+5^%sqlcq.DEMO.cls33.1 DEMO 10d3>zw %SNGetQueryStats=1 SQLCODE=0 rcc=1 DEMO 10d3>zwrite $this 45@%sqlcq.DEMO.cls33 ; <OREF> +----------------- general information --------------- | oref value: 45 | class name: %sqlcq.DEMO.cls33 | reference count: 6 +----------------- attribute values ------------------ | %CurrentResult = "45@%sqlcq.DEMO.cls33" | %CursorNumber = 1 | %ExtendedMetadata = $lb($lb("Sample.Person||ID","%Library.BigInt",18),$lb("Sample.Person||Name","%Library.String",10),$lb("Sample.Person||Random","%Library.Numeric",14)) | %Message = "" | %Metadata(0) = $lb(3,"ID",-5,19,"0",1,"ID","Person","Sample",0,$c(0,0,0,1,0,0,0,0,0,0,0,0,0),"Name",12,50,"0",0,"Name","Person","Sample",0,$c(0,0,0,0,0,0,0,0,0,0,0,0,0),"Random",2,15,"2",1,"Random","Person","Sample",0,$c(0,0,0,0,0,0,0,0,0,0,0,0,0)) | (%NextColumn) = 1 | %Objects = "" | %OutputColumnCount = 0 | %Parameters = "" | %ROWCOUNT = 0 | %ROWID = "" | %ResultColumnCount = 3 | %SQLCODE = 0 | (%SelectMode) = 0 | %StatementType = 1 | (%delock11) = "" | %routine = "" | CursorState = 1 | (ID1) = 1 | (IDO1) = "" | (NameO1) = "Newton,Olga Z." | (PpCallArgs1) = 10 | (Vrowcnt17) = 0 | (isolationMode) = 0 | (lockstat11) = 0 | (node2val21) = $lb("",82732383,"OptiPlex Media Inc.",34350,"V968","P1169","OR","E8259","Z4197","","Newton,Olga Z.","U4734","P4","U8988","W9047","992-61-9877",-92713954810893571,"2018-07-16 17:44:34") | (rowcnt) = 0 | (rowlimit) = 9223372036854775807 | (starttime) = 9966.938434 | (time) = .000001 | (vpRUNTIMEOUT3) = "Newton,Olga Z." +----------------- swizzled references --------------- | i%%PrivateTables = "" <Set> | r%%PrivateTables = "" <Set> | (i%%ProcCursor) = "" | (r%%ProcCursor) = "" | (i%%rsmd) = "" | (r%%rsmd) = "50@%SQL.StatementMetadata" +--------------- calculated references --------------- | %StatementTypeName <Get> | ID <Get> [ Aliases - id ] | Name <Get> [ Aliases - name,NAME ] | Random <Get> [ Aliases - random,RANDOM ] +----------------------------------------------------- DEMO 10d3>g Newton,Olga Z. 1 . try { Set rcc=$Random(5) BREAK Set i%RandomO1 = $Random(10)/rcc ^ <BREAK>%0AmBk1+5^%sqlcq.DEMO.cls33.1 DEMO 10d3>zwrite %SNGetQueryStats=1 SQLCODE=0 rcc=1 DEMO 10d3>g Adam,Emily G. 7 . try { Set rcc=$Random(5) BREAK Set i%RandomO1 = $Random(10)/rcc ^ <BREAK>%0AmBk1+5^%sqlcq.DEMO.cls33.1 DEMO 10d3>zw %SNGetQueryStats=1 SQLCODE=0 rcc=0 DEMO 10d3>g ; now we get the error [SQLCODE: <-350>] [%msg: <Unexpected error executing SqlCompute code for field 'Random': <DIVIDE>%0AmBk1+5^%sqlcq.DEMO.cls33.1>] 2 Rows(s) Affected statement prepare time(s)/globals/cmds/disk: 0.0003s/4/137/0ms execute time(s)/globals/cmds/disk: 44.5198s/37/839/0ms cached query class: %sqlcq.DEMO.cls33 --------------------------------------------------------------------------- [SQL]DEMO>>q DEMO> Y, como puedes ver, estos métodos de depuración actualmente también funcionan en IRIS. Esta es una práctica personal recomendada por Robert Cemper.
Artículo
Ricardo Paiva · 24 feb, 2023

Depuración web - Parte 2

En el [primer artículo](https://es.community.intersystems.com/post/depuraci%C3%B3n-web) hablé sobre probar y depurar aplicaciones web de Caché con herramientas externas. La segunda parte tratará sobre las herramientas de Caché. Estas son: * CSP Gateway y Webapp configuration * CSP Gateway logging * CSP Gateway tracing * ISCLOG * Custom logging * Session events * Output to device ### CSP Gateway y Webapp configuration En primer lugar, si estás depurando y, sobre todo, desarrollando una aplicación front-end, no necesitas *caching*. Es muy útil en un sistema de producción, pero no durante el desarrollo. Para desactivar el registro de una aplicación web, hay que ir a: SMP → Menu → Manage Web Applications → y definir Serve Files Timeout con la configuración igual a 0. Luego hacer clic en "Save". Después, hay que purgar el caché de la aplicación web. Para ello, ve a: SMP → System Administration → Configuration → CSP Gateway Management → System Status. Allí se encuentra la tabla "Cached Forms", la última fila es una línea Total, pulsa el botón borrar (con el punto) para borrar el caché de la aplicación web: ![](/sites/default/files/inline/images/snimok_26.png) ### CSP Gateway logging Ya que estamos hablando de CSP Gateway, este es compatible con el registro de las solicitudes de entrada (documentación). En la pestaña Default Parameters, especifica el nivel de registro deseado (por ejemplo, v9a) y guarda los cambios. v9a (consulta otras opciones en la documentación) registra todas las solicitudes HTTP a http.log en el directorio principal de Gateway. Este es un ejemplo de solicitud capturada: GET /forms/form/info/Form.Test.Person HTTP/1.1 Host: localhost:57772 User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:53.0) Gecko/20100101 Firefox/53.0 Accept: application/json, text/plain, */* Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Referer: http://localhost:57772/formsui/index.html Cookie: CSPSESSIONID-SP-57772-UP-forms-=001000000000yxiocLLb8bbc9SVXQJC5WMU831n2sENf4OGeGa; CSPWSERVERID=144zwBTP Dnt: 1 Connection: keep-alive Cache-Control: max-age=0 <> También hay opciones para registrar el rendimiento. Los resultados pueden escribirse en un archivo o visualizarse desde la pestaña View Event Log. ### CSP Gateway tracing Por último, se pueden rastrean las solicitudes y respuestas en la pestaña View HTTP Trace de CSP Gateway. Activa el rastreo y las solicitudes comenzarán a ser capturadas inmediatamente. No olvides apagarlo después de realizar la depuración. Este es un ejemplo de sesión de depuración: ![](/sites/default/files/inline/images/snimok_27.png) Nota: utiliza el rastreo si puedes identificar cuál es el problema y reproducirlo fácilmente. Utiliza el registro para recoger estadísticas, elaborar perfiles de rendimiento, etc. Además, la mayoría de los servidores web ofrecen herramientas de registro y seguimiento del rendimiento. ### ISCLOG CSP Gateway es útil para determinar problemas de red y hacer seguimiento del rendimiento, pero para registrar lo que ocurre dentro de Caché se necesitan otras herramientas. Una de las herramientas más versátiles es ISCLOG. [Documentación](http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GCSP_logging). Es un *global* que puede almacenar información sobre el procesamiento de las solicitudes actuales. Para iniciar el registro, ejecuta: set ^%ISCLOG = 2 Y para terminar el registro, ejecuta: set ^%ISCLOG = 0 Este es el ejemplo de una solicitud: ^%ISCLOG=0 ^%ISCLOG("Data")=24 ^%ISCLOG("Data",1)=$lb(2,"CSPServer","Header from CSP Size:3744 CMD:h IdSource:3","4664","FORMS","2017-06-07 10:49:21.341","%SYS.cspServer2","","") ^%ISCLOG("Data",1,0)="^h30000 "_$c(14,0,0)_"A" ^%ISCLOG("Data",2)=$lb(2,"CSPServer","[UpdateURL] Looking up: //localhost/forms/form/info path found: //localhost/forms/ Appl= "_$c(2,1,3,4)_"@"_$c(3,4,1,2,1,9,1)_"/forms/"_$c(2,1,3,4,1,2,1,2,1,2,4,3,4,1,2,1,9,1,7,1)_":%All"_$c(8,1)_"/forms"_$c(7,1)_"FORMS"_$c(2,1,2,1,3,4,1,2,1,2,1,3,4,1,2,1,4,4,132,3,3,4,2,3,4,2,2,1,4,4,16,14,2,4,3,4,1,3,4,1,2,1,3,4,1,2,1,16,1)_"Form.REST.Main"_$c(2,4,2,4),"4664","FORMS","2017-06-07 10:49:21.342","%CSP.Request.1","124","L3DfNILTaE") ^%ISCLOG("Data",3)=$lb(2,"CSPServer","[UpdateURL] Found cls: Form.REST.Main nocharsetconvert:  charset:UTF-8 convert charset:UTF8","4664","FORMS","2017-06-07 10:49:21.342","%CSP.Request.1","124","L3DfNILTaE") ^%ISCLOG("Data",4)=$lb(2,"CSPServer","[HTML] Determined request type","4664","FORMS","2017-06-07 10:49:21.342","%SYS.cspServer2","124","L3DfNILTaE") ^%ISCLOG("Data",4,0)=$lb("usesession",1,"i%Class","Form.REST.Main","i%Service","REST","NOLOCKITEM","","i%GatewayError","") ^%ISCLOG("Data",5)=$lb(2,"CSPSession","[%LoadData] Loading CSP session, nosave=0","4664","FORMS","2017-06-07 10:49:21.342","%CSP.Session.1","","L3DfNILTaE") ^%ISCLOG("Data",5,0)=$lb(900,,0,5567742244,$c(149)_"Ù"_$c(3)_"ó»à"_$c(127)_",½"_$c(149,10)_"\"_$c(18)_"v"_$c(128,135)_"3Vô"_$c(11)_"*"_$c(154)_"PÏG¥"_$c(140,157,145,10,131)_"*",2,"FORMS","001000000000L3DfNILTaE1cDBJNjyQdyLwKq4wCXP82ld8gic",,0,"ru","L3DfNILTaE",2,1,"/forms/",$lb("UnknownUser","%All,%Developer","%All,%Developer",64,-559038737),"","","","2017-06-07 10:48:51","2017-06-07 10:49:04","Basic ZGV2OjEyMw==","Mozilla/5.0 (Windows NT 10.0; WOW64; rv:53.0) Gecko/20100101 Firefox/53.0","","",0,"/forms/","","","",4,"","","","","http://localhost:57772/formsui/index.html") ^%ISCLOG("Data",6)=$lb(2,"CSPServer","[CSPDispatch]Requested GET /forms/form/info","4664","FORMS","2017-06-07 10:49:21.342","%SYS.cspServer","124","L3DfNILTaE") ^%ISCLOG("Data",7)=$lb(2,"CSPServer","[CSPDispatch] ** Start processing request newSes=0","4664","FORMS","2017-06-07 10:49:21.342","%SYS.cspServer","124","L3DfNILTaE") ^%ISCLOG("Data",7,0)="/forms/form/info" ^%ISCLOG("Data",8)=$lb(2,"CSPServer","[CSPDispatch] Service type is REST has-soapaction=0 nosave=0","4664","FORMS","2017-06-07 10:49:21.342","%SYS.cspServer","124","L3DfNILTaE") ^%ISCLOG("Data",9)=$lb(2,"CSPServer","[CSPDispatch]About to run page: Form.REST.Main","4664","FORMS","2017-06-07 10:49:21.342","%SYS.cspServer","124","L3DfNILTaE") ^%ISCLOG("Data",9,0)=$lb("UnknownUser","%All,%Developer","%All,%Developer",64,-559038737) ^%ISCLOG("Data",10)=$lb(2,"CSPServer","[callPage] url=/forms/form/info ; Appl: /forms/ newsession=0","4664","FORMS","2017-06-07 10:49:21.342","%SYS.cspServer","124","L3DfNILTaE") ^%ISCLOG("Data",11)=$lb(2,"CSPServer","[callPage]Imported security context ; User: UnknownUser ; Roles: %All,%Developer","4664","FORMS","2017-06-07 10:49:21.342","%SYS.cspServer","124","L3DfNILTaE") ^%ISCLOG("Data",12)=$lb(2,"CSPServer","[OutputCSPGatewayData]: chd=1;","4664","FORMS","2017-06-07 10:49:21.431","%CSP.Response.1","","L3DfNILTaE") ^%ISCLOG("Data",13)=$lb(2,"CSPResponse","[WriteHTTPHeaderCookies] Session cookie: CSPSESSIONID-SP-57772-UP-forms-=001000000000L3DfNILTaE1cDBJNjyQdyLwKq4wCXP82ld8gic; path=/forms/;  httpOnly;","4664","FORMS","2017-06-07 10:49:21.431","%CSP.Response.1","124","L3DfNILTaE") ^%ISCLOG("Data",14)=$lb(2,"CSPServer","[callPage] Return Status","4664","FORMS","2017-06-07 10:49:21.431","%SYS.cspServer","124","L3DfNILTaE") ^%ISCLOG("Data",14,0)=1 ^%ISCLOG("Data",15)=$lb(2,"CSPServer","[OutputCSPGatewayData]: chd=1;","4664","FORMS","2017-06-07 10:49:21.431","%CSP.Response.1","","L3DfNILTaE") ^%ISCLOG("Data",16)=$lb(2,"CSPServer","[Cleanup]Page EndSession=0; needToGetALicense=-1; nosave=0; loginredirect=0; sessionContext=1","4664","FORMS","2017-06-07 10:49:21.431","%SYS.cspServer","124","L3DfNILTaE") ^%ISCLOG("Data",17)=$lb(2,"CSPSession","[Cleanup] EndSession=0 nosave=0","4664","FORMS","2017-06-07 10:49:21.431","%SYS.cspServer","124","L3DfNILTaE") ^%ISCLOG("Data",18)=$lb(2,"CSPSession","[%SaveData] Saved: ","4664","FORMS","2017-06-07 10:49:21.431","%CSP.Session.1","","L3DfNILTaE") ^%ISCLOG("Data",18,0)=$lb(900,,0,5567742261,$c(149)_"Ù"_$c(3)_"ó»à"_$c(127)_",½"_$c(149,10)_"\"_$c(18)_"v"_$c(128,135)_"3Vô"_$c(11)_"*"_$c(154)_"PÏG¥"_$c(140,157,145,10,131)_"*",2,"FORMS","001000000000L3DfNILTaE1cDBJNjyQdyLwKq4wCXP82ld8gic",,0,"ru","L3DfNILTaE",2,1,"/forms/",$lb("UnknownUser","%All,%Developer","%All,%Developer",64,-559038737),"","","","2017-06-07 10:48:51","2017-06-07 10:49:21","Basic ZGV2OjEyMw==","Mozilla/5.0 (Windows NT 10.0; WOW64; rv:53.0) Gecko/20100101 Firefox/53.0","","",0,"/forms/","","","",5,"","","","","http://localhost:57772/formsui/index.html") ^%ISCLOG("Data",19)=$lb(2,"CSPServer","[Cleanup] Restoring roles before running destructor","4664","FORMS","2017-06-07 10:49:21.431","%SYS.cspServer","","L3DfNILTaE") ^%ISCLOG("Data",19,0)=$lb("UnknownUser","%All,%Developer","%All,%Developer",64,-559038737) ^%ISCLOG("Data",20)=$lb(2,"CSPServer","[Cleanup] End","4664","FORMS","2017-06-07 10:49:21.431","%SYS.cspServer","","L3DfNILTaE") ^%ISCLOG("Data",20,0)="<-Finish processing request->" ^%ISCLOG("Data",21)=$lb(2,"GatewayRequest","[CSPGWClientRequest] GWID: ed-pc:57772; Request: sys_get_system_metricsTimeout: 5","11112","%SYS","2017-06-07 10:49:23.141","%SYS.cspServer3","","") ^%ISCLOG("Data",22)=$lb(2,"GatewayRequest","[CSPGWClientRequest] GWID: 127.0.0.1:57772; Request: sys_get_system_metricsTimeout: 5","11112","%SYS","2017-06-07 10:49:23.141","%SYS.cspServer3","","") ^%ISCLOG("Data",23)=$lb(2,"GatewayRequest","[SendSimpleCmd:Server:Failed] WebServer: 127.0.0.1:57772; Gateway Server Request Failed","11112","%SYS","2017-06-07 10:49:23.141","%CSP.Mgr.GatewayMgrImpl.1","","") ^%ISCLOG("Data",23,0)=0 ^%ISCLOG("Data",24)=$lb(2,"GatewayRequest","[GetMetrics]","11112","%SYS","2017-06-07 10:49:23.141","%CSP.Mgr.GatewayMgrImpl.1","","") ^%ISCLOG("Data",24,0)="<-End Request Client->" Además, este es un script rápido para la salida de *global* a un archivo: set p="c:\temp\isclog.txt" open p:"NW" use p zw ^%ISCLOG close p   ### Custom logging Aunque las herramientas de registro predeterminadas son bastante buenas, tienen varios problemas: * Son genéricas y no están familiarizadas con tu solicitud * Las opciones más detalladas afectan al rendimiento * No están bien estructuradas, por lo que puede resultar difícil extraer información Por lo tanto, se pueden cubrir casos más específicos escribiendo sistemas de registro personalizados. Este es un ejemplo de una clase persistente que registra parte del objeto %request: /// Incoming request Class Log.Request Extends %Persistent { /// A string indicating HTTP method used for this request. Property method As %String; /// A string containing the URL up to and including the page name /// and extension, but not including the query string. Property url As %String(MAXLEN = ""); /// A string indicating the type of browser from which the request /// originated, as determined from the HTTP_USER_AGENT header. Property userAgent As %String(MAXLEN = ""); /// A string indicating the MIME Content-Type of the request. Property contentType As %String(MAXLEN = ""); /// Character set this request was send in, if not specified in the HTTP headers /// it defaults to the character set of the page it is being submitted to. Property charSet As %String(MAXLEN = ""); /// A <class>%CSP.Stream</class> containing the content submitted /// with this request. Property content As %Stream.GlobalBinary; /// True if the communication between the browser and the web server was using /// the secure https protocol. False for a normal http connection. Property secure As %Boolean; Property cgiEnvs As array Of %String(MAXLEN = "", SQLPROJECTION = "table/column"); Property data As array Of %String(MAXLEN = "", SQLPROJECTION = "table/column"); ClassMethod add() As %Status { set request = ..%New() quit request.%Save() } Method %OnNew() As %Status [ Private, ServerOnly = 1 ] { #dim %request As %CSP.Request #dim sc As %Status = $$$OK quit:'$isObject($g(%request)) $$$ERROR($$$GeneralError, "Not a web context") set ..charSet = %request.CharSet if $isObject(%request.Content) { do ..content.CopyFromAndSave(%request.Content) } else { set ..content = "" } set ..contentType = %request.ContentType set ..method = %request.Method set ..secure = %request.Secure set ..url = %request.URL set ..userAgent = %request.UserAgent set cgi = "" for { set cgi=$order(%request.CgiEnvs(cgi)) quit:cgi="" do ..cgiEnvs.SetAt(%request.CgiEnvs(cgi), cgi) } // Only gets first data if more than one data with the same name is present set data = "" for { set data=$order(%request.Data(data)) quit:data="" do ..data.SetAt(%request.Get(data), data) } quit sc } } Para añadir un nuevo registro a la tabla Log.Request, agrega una llamada a tu código: do ##class(Log.Request).add() Es un ejemplo muy básico, puede y debe ampliarse para registrar comentarios, variables o cualquier otra cosa que puedas necesitar. La principal ventaja de este enfoque es la posibilidad de ejecutar consultas SQL sobre los datos registrados.  Para obtener más información sobre cómo crear tu propio sistema de registro, consulta [este artículo](https://es.community.intersystems.com/post/logging-usando-macros-en-intersystems-cach%C3%A9). ### Session events La clase Event (Evento) es una clase que define las interfaces que se llaman durante la vida de un objeto [%CSP.Session](http://docs.intersystems.com/latest/csp/documatic/%25CSP.Documatic.cls?PAGE=CLASS&LIBRARY=%25SYS&CLASSNAME=%25CSP.Session). Para utilizarla, hay que subclasificar [%CSP.SessionEvents](http://docs.intersystems.com/latest/csp/documatic/%25CSP.Documatic.cls?PAGE=CLASS&LIBRARY=%25SYS&CLASSNAME=%25CSP.SessionEvents) e implementar el código del método que quieres ejecutar. A continuación, en la configuración de la aplicación CSP, hay que establecer la clase de evento en la clase que has creado. Las siguientes devoluciones de llamada están disponibles: OnApplicationChange OnEndRequest OnEndSession OnLogin OnLogout OnStartRequest OnStartSession * OnTimeout Por ejemplo, los custom loggings comentado anteriormente pueden invocarse desde estos métodos. ### Output to device Una de las opciones más sencillas - un método de utilidad CSP que muestra todos los objetos como respuesta. Solo hay que añadir esto a cualquier parte de tu código: set %response.ContentType = "html" do ##class(%CSP.Utils).DisplayAllObjects() return $$$OK   ### Conclusión Hay varias herramientas que se puede utilizar para depurar las aplicaciones web. Elige la más adecuada para la tarea que vayas a realizar.   Y vosotros... ¿tenéis algún consejo o truco para depurar aplicaciones web desde Caché?