1. Locates and accesses application globals
Key Points
- Globals (^name): Persistent multidimensional arrays stored in databases; the fundamental storage mechanism in InterSystems IRIS
- ZWRITE: Displays the contents of a global or subtree in a readable format
- $DATA: Tests whether a global node exists and whether it has descendants
- $ORDER: Iterates through global subscripts at a given level
- $GET: Safely retrieves a global node value, returning a default if the node does not exist
- Management Portal: System Explorer > Globals provides a visual global viewer and editor
Detailed Notes
Understanding Global Structure
Globals are sparse, hierarchical, multidimensional arrays stored on disk. They persist across process boundaries and system restarts. Every persistent class stores its data in globals -- understanding the underlying global structure is essential for debugging and performance analysis.
// Setting global nodes
SET ^Customer(1, "Name") = "John Smith"
SET ^Customer(1, "Email") = "john@example.com"
SET ^Customer(2, "Name") = "Jane Doe"
SET ^Customer(2, "Email") = "jane@example.com"
// Display entire global tree
ZWRITE ^Customer
// Output:
// ^Customer(1,"Email")="john@example.com"
// ^Customer(1,"Name")="John Smith"
// ^Customer(2,"Email")="jane@example.com"
// ^Customer(2,"Name")="Jane Doe"
Navigating Globals with $ORDER
$ORDER returns the next subscript at a given level, enabling traversal of global nodes:
// Iterate through all customer IDs
SET id = ""
FOR {
SET id = $ORDER(^Customer(id))
QUIT:id=""
WRITE "Customer ", id, ": ", ^Customer(id, "Name"), !
}
Using $DATA to Check Node Existence
$DATA returns a value indicating whether a node exists and/or has children:
- 0 = node does not exist and has no descendants
- 1 = node exists but has no descendants
- 10 = node does not exist but has descendants
- 11 = node exists AND has descendants
IF $DATA(^Customer(1)) {
WRITE "Customer 1 exists", !
}
// Use $DATA to check before accessing
IF $DATA(^Config("settings", "timeout"))#2 {
SET timeout = ^Config("settings", "timeout")
}
Management Portal Global Viewer
Navigate to System Explorer > Globals to browse, search, and edit globals. You can:
- View globals across all namespaces
- Filter by global name prefix
- Drill into subscript levels
- Export and import global data
- Delete globals or individual nodes
Locating Class Data Globals
Persistent classes store data in globals named after the class. Use the storage definition in Studio/VS Code to find the exact global name:
// For class MyApp.Customer, data is typically in:
// ^MyApp.CustomerD (data global)
// ^MyApp.CustomerI (index global)
// ^MyApp.CustomerS (sequence counter)
ZWRITE ^MyApp.CustomerD
Documentation References
2. Uses logging to track application data
Key Points
- ^%ISCLOG: Built-in diagnostic logging used by InterSystems framework code (CSP gateway, web services, productions); controlled by the ^%ISCLOG global, entries stored in ^ISCLOG in %SYS namespace
- Custom logging globals: Application-defined globals (e.g., ^MyApp.Log) for structured application-level logging
- %Library.File: Write log data to external files for integration with external log management systems
- $HOROLOG / $ZTIMESTAMP: Timestamps for log entries; $ZTIMESTAMP includes UTC time with fractional seconds
- Log levels: Implement severity levels (ERROR, WARN, INFO, DEBUG) for filtering and analysis
Detailed Notes
^%ISCLOG: Built-In Diagnostic Logging
^%ISCLOG is a diagnostic logging facility used by InterSystems framework code — CSP gateway, web services, Interoperability productions, and other system components. It is not typically called directly by application code; instead, system code calls the $$$ISCLOG and $$$SysLog macros (defined in %occDiagnostics.inc) to generate log entries.
How it works:
- The ^%ISCLOG global (top-level value) controls the logging level: 0=disabled, 1=errors only, 2=warnings and errors, 3=all messages (most verbose)
- Log entries are written to the ^ISCLOG global (without the % prefix) in the %SYS namespace
- Entries can be viewed using the %Library.SysLog class or by doing
ZWRITE ^ISCLOGin the %SYS namespace
// Enable diagnostic logging at maximum verbosity
SET ^%ISCLOG = 3
// Optionally limit the log size (default is unlimited)
SET ^%ISCLOG("MaxLogEntries") = 10000
// After troubleshooting, disable logging (impacts performance when enabled)
SET ^%ISCLOG = 0
// View log entries — switch to %SYS namespace first
ZN "%SYS"
ZWRITE ^ISCLOG
// Or use the %Library.SysLog class to query entries programmatically
SET rs = ##class(%Library.SysLog).FindAll()
WHILE rs.%Next() {
WRITE rs.%Get("TimeStamp"), " ", rs.%Get("Message"), !
}
Important: Remember to set ^%ISCLOG back to 0 after troubleshooting, as verbose logging consumes disk space and impacts performance.
Building Custom Application Logs
For application-specific logging, create structured globals with timestamps, severity, and context. Unlike ^%ISCLOG (which is for system/framework diagnostics), custom logging globals give you full control over what your application records:
ClassMethod Log(level As %String, message As %String, context As %String = "")
{
SET seq = $INCREMENT(^MyApp.Log)
SET ^MyApp.Log(seq) = $LISTBUILD(
$ZTIMESTAMP,
level,
$USERNAME,
$JOB,
context,
message
)
// Also maintain a date-based index for efficient retrieval
SET date = +$HOROLOG
SET ^MyApp.Log("date", date, seq) = ""
}
// Usage
DO ##class(MyApp.Logger).Log("INFO", "Order created", "OrderId=1234")
DO ##class(MyApp.Logger).Log("ERROR", "Payment failed: timeout", "OrderId=1234")
Retrieving and Analyzing Log Data
// Iterate through recent log entries
SET id = ""
FOR {
SET id = $ORDER(^MyApp.Log(id), -1) // Reverse order (newest first)
QUIT:id=""
SET entry = ^MyApp.Log(id)
SET timestamp = $LISTGET(entry, 1)
SET level = $LISTGET(entry, 2)
SET message = $LISTGET(entry, 6)
WRITE id, " [", level, "] ", message, !
}
File-Based Logging
For integration with external log management systems, write to files:
SET file = ##class(%Library.File).%New("/var/log/myapp/application.log")
DO file.Open("WSA") // Write, Stream, Append
DO file.WriteLine($ZDATETIME($ZTIMESTAMP, 3, 1) _ " [INFO] Application started")
DO file.Close()
Documentation References
3. Adds and tracks metrics for performance monitoring
Key Points
- $SYSTEM.Process: Provides per-process performance counters (global references, lines executed, etc.)
- ^IRIS.Temp globals: Temporary globals for storing transient metrics without journaling overhead
- $SYSTEM.SQL.Stats: SQL query statistics and performance data
- ^%SYS.MONLBL: Line-by-line monitoring data for code profiling
- %SYSTEM.Process.GetMetric(): Retrieve specific process-level metrics programmatically
Detailed Notes
Process-Level Metrics with $SYSTEM.Process
$SYSTEM.Process provides access to detailed process performance counters:
// Get global references count for current process
WRITE $SYSTEM.Process.GlobalReferences(), !
// Get lines of ObjectScript executed
WRITE $SYSTEM.Process.LinesExecuted(), !
// Get disk read count
WRITE $SYSTEM.Process.DiskReadCount(), !
// Measure performance of a code block
SET startGref = $SYSTEM.Process.GlobalReferences()
SET startLines = $SYSTEM.Process.LinesExecuted()
SET startTime = $ZHOROLOG // High-precision timestamp
// ... code being measured ...
DO ##class(MyApp.DataLoader).ProcessBatch()
SET elapsedTime = $ZHOROLOG - startTime
SET globalRefs = $SYSTEM.Process.GlobalReferences() - startGref
SET linesExec = $SYSTEM.Process.LinesExecuted() - startLines
WRITE "Time: ", elapsedTime, " seconds", !
WRITE "Global refs: ", globalRefs, !
WRITE "Lines executed: ", linesExec, !
Using ^IRIS.Temp for Transient Metrics
^IRIS.Temp globals reside in the IRISTEMP database, which is never journaled. This makes them ideal for storing performance counters and temporary metrics without impacting transaction performance or journal volume:
// Track API call counts by endpoint
SET ^IRIS.Temp.Metrics("API", endpoint, $PIECE($HOROLOG, ",", 1)) =
$INCREMENT(^IRIS.Temp.Metrics("API", endpoint, $PIECE($HOROLOG, ",", 1)))
// Track method execution times
SET startTime = $ZHOROLOG
// ... method execution ...
SET elapsed = $ZHOROLOG - startTime
SET ^IRIS.Temp.Metrics("Timing", methodName, $JOB) = elapsed
SQL Performance Statistics
Use $SYSTEM.SQL.Stats to analyze SQL query performance:
// Get SQL statistics
DO $SYSTEM.SQL.Stats.DisplayStats()
// For a specific query, examine the query plan
SET stmt = ##class(%SQL.Statement).%New()
SET sc = stmt.%Prepare("SELECT * FROM MyApp.Customer WHERE State = ?")
DO stmt.%ShowPlan()
Line-by-Line Monitoring
Enable line-by-line monitoring to profile code execution:
// Enable monitoring for a routine
DO $SYSTEM.Monitor.LineLevelMonStart("MyApp.DataLoader")
// Run the code
DO ##class(MyApp.DataLoader).ProcessBatch()
// Stop monitoring and view results
DO $SYSTEM.Monitor.LineLevelMonStop()
// Results stored in ^%SYS.MONLBL
ZWRITE ^%SYS.MONLBL
Building a Performance Dashboard
Combine metrics for a comprehensive view:
ClassMethod CollectMetrics() As %Status
{
SET timestamp = $ZTIMESTAMP
SET metrics = $LISTBUILD(
$SYSTEM.Process.GlobalReferences(),
$SYSTEM.Process.LinesExecuted(),
$SYSTEM.Process.DiskReadCount(),
$SYSTEM.Process.JournalEntryCount()
)
SET ^IRIS.Temp.PerfLog($INCREMENT(^IRIS.Temp.PerfLog)) =
$LISTBUILD(timestamp) _ metrics
RETURN $$$OK
}
Documentation References
Exam Preparation Summary
Critical Concepts to Master:
- Global navigation: Fluency with $ORDER, $DATA, $GET, and ZWRITE for examining and traversing globals
- $DATA return values: Know what 0, 1, 10, and 11 mean and how to use modular arithmetic (#2, #10) to test specific conditions
- Logging strategies: Understand ^%ISCLOG for system/framework diagnostics vs custom logging globals for application data vs file-based logging for external integration
- Performance counters: Know how to use $SYSTEM.Process methods to measure global references, lines executed, and timing
- ^IRIS.Temp: Understand that IRISTEMP globals are not journaled, making them ideal for transient data and metrics
Common Exam Scenarios:
- Writing $ORDER loops to iterate through a global structure
- Interpreting $DATA return values for various global node configurations
- Choosing appropriate logging mechanisms for different scenarios
- Measuring code performance using $SYSTEM.Process counters and $ZHOROLOG
- Identifying the correct global name for a persistent class's data storage
- Using ZWRITE to debug global contents
Hands-On Practice Recommendations:
- Create globals with various structures and practice navigating them with $ORDER at different subscript levels
- Build a simple logging class and test it with different log levels
- Use $SYSTEM.Process.GlobalReferences() to compare performance of different data access patterns
- Explore the Management Portal's global viewer (System Explorer > Globals) across different namespaces
- Enable ^%ISCLOG at different levels, then switch to %SYS namespace to examine ^ISCLOG entries
- Write code that stores metrics in ^IRIS.Temp and verify that the data is not journaled