Skip to main content
Version: Next — unreleased

Log levels and object structure

Use this reference to interpret Delphi log records and the audit events written by their individual producers. The common fields below describe the shared Node logger used by Delphi services. Native telephony logs, SIP captures, browser console messages and trace spans have their own formats. Do not apply one JSON schema to all of them.

Availability: To read raw log records, you need access to your deployment's log backend, for example SigNoz. To read audit records in Admin Settings → Access Logs, you need Super Admin or Super Viewer. Security tags on audit records are available from 0.9.18.

Start with the record you are investigating:

Severity levels​

The standard severity labels in SigNoz are TRACE, DEBUG, INFO, WARN, ERROR and FATAL. Use these uppercase names when interpreting the normalized severity. Severity is separate from the event's logType, action or event; do not change the case of those fields.

LevelReading guidance
TRACEFinest diagnostic detail
DEBUGDiagnostic detail for investigation
INFOInformational activity
WARNA warning requiring context; not automatically a failed call
ERRORAn error event; inspect its scope and recovery outcome
FATALHighest severity; inspect the producer's failure context

Raw JSON and normalized telemetry have different representations: the shared Pino logger still writes lowercase level values such as info, while direct OpenTelemetry export sets uppercase severityText. The configured collector pipelines normalize the severity shown in SigNoz; a retained source level attribute can still be lowercase. This is not a second severity convention. Missing severity defaults to INFO in the configured normalization path; that fallback alone does not prove that the underlying event succeeded.

Historical records are not rewritten. A SigNoz time range containing older records can therefore offer both info and INFO. If newly ingested records still have lowercase normalized severity, inspect the actual collector path and deployed configuration. Narrow the query to records emitted after that collector was updated, and distinguish the record's normalized severity from its raw level attribute. Do not assume an empty-label fallback also normalizes existing lowercase labels.

For services that use the shared level resolver, <SERVICE>_LOG_LEVEL takes precedence over LOG_LEVEL, with info as the fallback. Values are trimmed and case-normalized. An explicitly set but empty or invalid service override falls back to info; it does not fall through to the global value. The resolver also maps verbose, vai-verbose and verb to debug, notice to info, warning to warn, err to error, and critical to fatal.

This describes the resolver, not proof that every container uses it. Check the service's configuration before changing verbosity. In the structured domain logger, the stdout Pino threshold is not a single global switch for every destination: direct telemetry emission and configured database persistence have separate paths. Do not rely on a stdout threshold to prevent a record from being stored elsewhere.

Set the level for each component​

In the delivered Compose definitions, each container receives LOG_LEVEL from its own deployment variable. Use the labels trace, debug, info, warn, error and fatal. Native telephony components translate the label into their own scale at container start, so a changed value needs a restart of that container.

ComponentVariableRange and defaultEffect
TelAPITELAPI_LOG_LEVELinfoThreshold of the shared Node logger.
ScalerSCALER_LOG_LEVELinfoThreshold of the shared Node logger.
TaskerTASKER_LOG_LEVELinfoThreshold of the shared Node logger.
TelPhiTELPHI_LOG_LEVELNo Compose default; the logger falls back to infoThreshold of the shared Node logger.
AudioProcAUDIOPROC_LOG_LEVELNo Compose default; the service falls back to infoPython logging level. trace is treated as debug and fatal as critical.
TelSys (internal telephony)TELSYS_LOG_LEVELinfoSelects the native logger channels. error or fatal: errors only. warn: warnings and errors. info: notices, warnings, errors and DTMF. debug or trace: additionally verbose output.
TelPro (SIP edge)TELPRO_LOG_LEVELinfoFilters the edge's structured Delphi records, and sets the proxy's native verbosity: error or fatal to 0, warn to 1, info or debug to 2, trace to 3. The extra label silly sets 4.
Media proxyRTPENGINE_LOG_LEVELinfoNative syslog level: error or fatal to 3, warn to 4, info to 6, debug or trace to 7. A numeric value is passed through unchanged.
WebRTC gateway, when deployedWEBRTC_LOG_LEVELinfoNative debug and JSON-log level: fatal to 1, error to 2, warn to 3, info to 4, debug or trace to 7.

Two details matter when you lower verbosity on the telephony components. SIP packet capture on the internal telephony service uses the native verbose channel; while that capture is enabled, verbose output stays on at every level. On the SIP edge, debug does not raise the proxy's own native verbosity above info; it adds the structured Delphi debug records only. Use trace or silly for proxy internals, and only for a bounded troubleshooting window.

An unknown label falls back to the info behavior for the media proxy, the WebRTC gateway and the internal telephony service. Confirm the effective level in the component's start-up output instead of assuming the variable was applied. For sensitive content in diagnostic output, see Protect diagnostic logs.

Event families​

The structured logger provides these event families. Fields depend on the family and emitting call site; not every service emits every family.

logTypePayload responsibility
systemComponent activity, action, status and optional metadata
errorComponent, error type, message, recoverability and optional context
conversationConversation event and actor, with applicable text/timing fields
telephonyChannel/call/bridge events and related telephony identifiers
audioDirection, format and audio processing details
model_usageProvider/model usage, service type and available usage measurements
realtime_usage, realtime_aggregated_usageIndividual or aggregated realtime usage
performanceOperation duration and optional resource measurements
vm, sandboxExecution environment and sandbox activity
auditActor, action, category, target and details; recordKind is Audit
cdrCall summary; see CDR fields and interpretation

A service logger can emit ordinary structured messages without a domain logType. Do not discard such records simply because they are not in this table.

Common fields and representations​

MeaningStructured domain entryImportant boundary
Timetimestamp, milliseconds since the Unix epochOrdinary service-Pino output and OpenTelemetry have their own timestamp representation
SeverityRaw lowercase levelCanonical SigNoz severity is uppercase; direct OpenTelemetry export uses uppercase severityText and numeric severity
Event familylogTypeDoes not replace family-specific eventType, event, action or operation
Producerservice, where configured; optional componentTelemetry resource identity and database columns are separate representations
Human-readable descriptionGenerated stdout msg; entry may contain messageDirect export uses the log body; queryable attributes carry structured data
Application/call contextOptional appId, callId, channelId, sessionId, turnIdAvailability depends on where the event is emitted
Trace contexttrace_id and span_id from the selected/active spanNot arbitrary aliases for a business transaction identifier
Event detailsFamily-specific fields and optional metadata, context or detailsA field absent from one family is not automatically an error

In the standard structured-logger configuration, direct export keeps safe nested metadata and context objects and also promotes their non-null fields into attributes when the name is not already occupied. Trace-context aliases are stripped rather than accepted from those payloads. Selected audit details fields are also promoted for filtering. Later logger reconfiguration can change field placement: the pretty metadata branch promotes fields and removes the nested metadata object before direct export, while database persistence receives the separate redacted entry. The readable body alone is therefore not a complete field reference.

Database projection​

When database logging is enabled and a persistence sink is configured, audit entries go to the audit store. Other domain entries go to the application log store, whose data contains the entry and whose searchable columns project selected fields. Its event column chooses eventType, then action, then event. A missing sink or persistence failure can leave a visible log without a corresponding database row.

The audit database projection keeps timestamp, action, category, actor, target/application and details; it is not a verbatim copy of the exported log record. Do not assume telemetry severity or delivery metadata is present as an audit table column.

Correlation​

Keep these identities distinct:

  • Call/application context identifies the product activity when supplied by the producer.
  • Trace/span context identifies telemetry execution relationships. The shared logger binds this from its selected or active span, rather than trusting nested payload aliases.
  • SIP identifiers belong to signaling records and can require explicit mapping to application context.
  • Transaction identifiers belong to their integration contract. They are not guaranteed on every call or event, and a transaction identifier is not automatically a valid trace ID.

Start an investigation with a known identifier, service and time range. Inspect the stored field location before writing a query: a JSON field, telemetry attribute and database column may have different names or nesting. Do not assume one identifier returns every event across all services.

Service-specific investigation fields​

Use these starting points for TelAPI, Tasker and Scaler service messages. They are representative investigation paths, not an exhaustive message catalogue or a new logType taxonomy. Begin with the actual service identity, instance and bounded time range; inspect one record before choosing attribute filters. These services use ordinary service-Pino records and component child loggers, so requiring a domain logType can hide relevant evidence. Message text and optional fields vary by producer and version. Follow delivery diagnosis if expected records are missing.

TelAPI: follow one HTTP request​

TelAPI's Fastify server uses the service logger, enables request logging and labels the request identifier requestId. It accepts the x-request-id header for this purpose. Start with the identifier returned by the error response, the affected instance and request time; inspect the corresponding request/error records. A request ID is not an authenticated caller identity, a trace ID or a guarantee that separate requests have unique values.

The error handler distinguishes these paths:

  • Rate limit exceeded is WARN and includes requestId and rateLimitKey (the request URL); the handler returns 429. Inspect the active limiter and request class using API rate limits before changing capacity or retrying aggressively.
  • Validation error is WARN for Fastify validation failures and returns 400. Inspect the permitted error details and correct the input. An application AppError, including an application validation error, follows the separate ERROR path; do not infer the entire cause from severity alone.
  • Unhandled error is ERROR and returns 500. Preserve the request ID, instance, time and safe error context for investigation. Other AppError records use the application's error message and status code.

Not every route error is necessarily emitted by this shared handler. HTTP request completion also does not prove that a background job, browser session or downstream call succeeded. Continue with the relevant session/job/call identifier when the response or subsequent producer supplies it; do not manufacture a trace link from requestId.

Tasker: separate scheduling from execution​

Start with component=scheduler when a recurring job was never created. Initialized schedule nextRunAt carries name and nextRunAt; Failed to parse cron expression carries name, cronExpr and error. Created job from cron schedule carries name, type and nextRunAt; it indicates enqueueing/schedule advancement, not successful execution. This message does not supply a jobId. Match the intended schedule and time to Job History before following its worker records.

Use component=worker and the actual jobId to follow execution. Processing job includes type, attempt and maxAttempts; Job completed includes type and duration in milliseconds. Job failed includes the job/type, attempt limits, duration and error text, with optional stack or AWS metadata. The failure line alone does not mean the job is terminal: inspect the persisted status, next-attempt time and any Job moved to DEAD after exhausting retries message. A handler can return a skip or unsuccessful business result without throwing, so completion still requires inspecting the job result and external outcome.

The executor component dispatches the registered job type; handler components add their own fields and messages. Trace restoration uses the producer-supplied traceId/parentSpanId when valid, rather than treating a job ID as a trace ID. For scheduling, retries and safe recovery, use background jobs; avoid replaying work merely because one log record is absent.

Messages about job leases and completion:

  • Failed to renew job lease identifies a lease-write problem. Inspect the current owner and attempt before recovery.
  • Retrying completion write without rerunning handler refers to saving an already completed handler result, not replaying the handler.
  • Completion could not be persisted; leaving job for recovery means an ordinary job can remain RUNNING although the handler succeeded.
  • Job completed but cron summary update failed distinguishes the job outcome from the schedule's summary.

Reconcile the stored job, lease and external effect before restarting or submitting new work. Password reminders have their own uncertain-delivery protection.

Scaler: follow authority, decision and provider result​

Use component=leader-election to establish which instance could act. Acquired leadership, Lost leadership and Renewed leadership lock carry an explicit instanceId. Error renewing leadership lock instead carries error; correlate it through the service-instance context (service.instance.id) and time rather than requiring an instanceId field on that error record. Routine renewal records are DEBUG, so their absence at INFO does not prove a lost leader.

Then inspect component=scaling. Found server groups with scaling enabled reports a count. It is not a request to add that many instances. Error evaluating group carries groupId, groupName and error. Skipping autoscale while rolling refresh owns surge capacity carries group identifiers and role, explaining why normal autoscaling can pause during a refresh. Follow the affected group through capacity and maintenance to compare the decision with healthy/provisioning capacity, utilization, cooldown and actual heartbeats.

Use component=script-executor for provider execution. Executing scale up script carries scriptPath, groupName and serverName; completion carries the returned serverId and privateIp. Scale-down records use groupName/serverId at invocation and serverId at completion. Scale up script stderr output or Scale down script stderr output is WARN but can precede successful completion; a stderr line alone is not a failed provider operation. Explicit script-failure records carry error context and the script path.

A completed provider script does not certify that the replacement registered, became healthy or accepted traffic. Verify runtime membership, heartbeat and retained serving capacity using the linked maintenance procedure. Missing/failed status scripts can return unknown; discovery failures can return an empty list, neither of which proves the instance or fleet does not exist. Keep script output, internal addresses and error metadata within the approved diagnostic audience.

Audit transport markers​

For structured audit events, recordKind: Audit and logType: audit identify the record family. Direct export is marked log_transport: otlp_direct and audit.delivery_role: canonical. The stdout record uses log_transport: stdout_filelog; its delivery role is mirror when direct export is enabled and canonical otherwise. These markers describe the producer's routing intent, not confirmed delivery or successful deduplication by a deployed collector. The Web, API and Ops collectors drop the copy marked mirror.

From 0.9.18, an audit row that the web application writes to the database is also sent to SigNoz after the change commits. That copy carries auditLogId, the database row ID. See Audit records in SigNoz.

Reconstruct an audited change​

Use an audit record to establish who attempted or performed a change, which resource it concerned and what outcome was recorded. A row is evidence from a particular producer; it is not automatically a complete history, proof of the actor's present authority or an external-delivery receipt.

Start with the existing Access Logs and export procedure for visibility, time filters and delivery checks. The Access Logs feed includes only selected categories: a missing application, configuration or other excluded category there does not establish that no audit row exists. Use the appropriately authorized resource history or operator investigation; do not broaden permissions merely to make the row appear.

Audit action catalogue​

The audit category groups records for Access Logs and the database: AUTH, ROLE_MANAGEMENT, USER, SIP_TRUNK, EGRESS_TRUNK, EGRESS_ROUTE, PLATFORM_SECRET, FLOW, NUMBER, SECURITY and SYSTEM. From 0.9.18, the shared logger also stamps audit records with security_event: true and a security_category, unless the producer already set security_event. The export collector uses these tags; see Verify security event export and retention.

Actionsecurity_category
LOGIN, LOGIN_FAILED, LOGOUT, SESSION_END, PASSWORD_CHANGE, PASSWORD_RESET_REQUESTED, PASSWORD_RESET_SUCCEEDED, PASSWORD_RESET_BY_SETUP_CLI, ACCOUNT_LOCKED, ACCOUNT_UNLOCKEDauthentication
REJECTED_CONNECTION, ACCESS_DENIED, PRIVILEGE_ESCALATION_ATTEMPT, PLATFORM_ROLE_CHANGE_STARTED, PLATFORM_ROLE_CHANGE_SUCCEEDED, PLATFORM_ROLE_CHANGE_DENIED, PLATFORM_ROLE_SESSION_CLEANUP_FAILED, SUPER_ADMIN_RECOVERY_ELEVATED, SUPER_ADMIN_RECOVERY_DENIEDauthorization
SYSTEM_MANIPULATION_ATTEMPT, SECURITY_LOGS_ACCESSED, SECURITY_POLICY_MODIFIEDaudit
CREATE, UPDATE, DELETENot tagged; not a security event

An action outside this list is still tagged security_event: true. Its security_category then follows the audit category: AUTH gives authentication, ROLE_MANAGEMENT gives authorization, and any other category leaves it empty. The security_category is a separate vocabulary from the audit category; do not use one in place of the other.

Separate the actor from the affected resource​

Field or blockInterpretation and limits
timestamp, action, categoryRecorded event instant and producer classification. The database field is timestamp, not a universal time column. A category/action catalogue does not guarantee every action is emitted by every service.
userId, usernameIdentity/name chosen by the producer. Usually the acting account, but do not assume every legacy or specialized producer uses this convention. A display name is not an immutable identity.
entityId, appIdAffected resource and application scope where supplied. Neither is automatically the acting user's ID. A platform event may have no application ID.
details.changedByFor callers using actor enrichment: {userId, name, email} from the session actor. The helper does not rewrite the top-level identity fields.
details.actor, details.targetEmailPrivileged-role events use a separate actor block and target email; correlate their entityId with the affected account. Do not expect every event to contain both actor representations.
details.actorRoleAtEventThe role captured by participating producers at event time. It can differ from the account's present role and may be absent from historical/other records.
details.old, details.newProducer-selected before/after objects, not a universal full database snapshot or an executable rollback. Inspect the actual keys and redaction.
details.outcome, details.reasonWhere supplied, explain success/denial or recovery conditions. Absence does not imply success.
details.ip, details.userAgentAvailable request metadata. The shared helper takes the first x-forwarded-for value, falling back to x-real-ip; these fields depend on correct trusted ingress/header handling, not independent proof of a person's physical location.

For example, a privileged-role event records the acting session user in top-level identity fields and details.actor; entityId identifies the account whose role is being changed. A user UPDATE can instead carry selected old/new account objects plus details.changedBy. Compare the object IDs before interpreting whose role or status changed.

For ROLE_MANAGEMENT and USER rows, Access Logs shows the affected account in the User column and the acting administrator under Changed by in Details. Recorded details.targetUserId, details.targetName and details.targetEmail describe that target; top-level userId and username still describe the actor. Do not infer that the person displayed in User performed the change.

For older role/user rows with neither target name nor email, the read API can resolve entityId against the current user record. This enriches the returned display without rewriting the stored audit row; it is not proof of the target's historical name or email. A deleted or unresolved account can remain an ID or an empty display. Search filters apply to stored fields before this enrichment, so a name visible on an older row is not necessarily searchable as historical target text.

Missing fields are meaningful limits. Unauthenticated paths may have no verified actor, and missing headers cannot produce an origin address. The presence of an actor-context helper does not guarantee every event includes all its fields. Do not turn a missing email/IP into a fabricated identity or assert that every record meets a compliance requirement.

Read a sequence rather than one success label​

  1. Record the event time, category, action, actor/resource identifiers and available reason/outcome. Keep the original event's role context when investigating later demotion or privilege changes.
  2. For privileged-role operations, distinguish PLATFORM_ROLE_CHANGE_STARTED, PLATFORM_ROLE_CHANGE_SUCCEEDED and PLATFORM_ROLE_CHANGE_DENIED. A started event is not successful completion. A subsequent PLATFORM_ROLE_SESSION_CLEANUP_FAILED identifies a separate session-cleanup problem; inspect its details and current account/session state rather than treating all stages as one atomic success. This producer records sessionsInvalidatedByVersion: true and redisSessionTrackingCleared: false: a Redis cleanup failure is not evidence that database session-version invalidation was undone.
  3. Compare the permitted current resource view with the selected old/new values. A later edit can explain why current state differs; the audit row is not a lock on the resource or a restore command.
  4. Distinguish record persistence from telemetry. Some producers write directly to the audit database; the shared logger's configured persistence/export path is separate. A database row can exist without a matching SigNoz record, and logger output can exist without successful database persistence.
  5. If the evidence is incomplete, preserve the bounded record/time/IDs and escalate to the resource owner. Do not repeat a password, role or security mutation solely to recreate evidence.

Exceptional setup recovery uses SUPER_ADMIN_RECOVERY_ELEVATED and SUPER_ADMIN_RECOVERY_DENIED, rather than the ordinary role-challenge sequence. Follow Super Admin recovery verification to correlate the asserted operator, change reference, target and result. A successful recovery audit is distinct from subsequent sign-in and email delivery.

Audit severity is independent of business outcome. The shared authentication logger defaults to INFO; the security helper defaults to WARN but permits callers to override it. Reading Access Logs itself records SECURITY_LOGS_ACCESSED at INFO. A warning is not proof of compromise, and INFO does not prove an operation succeeded.

Use account and role administration for the authorized action and recovery procedure. Use security export for classifier coverage, filtering, retention and downstream receipt. Keep before/after objects and actor details within their authorized audience when sharing diagnostic examples; arbitrary nested business data is not automatically safe because the record is an audit event.

Interpret a Call Detail Record​

A CDR is a best-effort summary emitted by TelPhi's call-end handling with logType: cdr and schemaVersion: '1.0'. Use it to connect a call's timing, technical finalization and available provider usage. It is not a complete billing ledger, proof of business success or a guarantee of one record for every attempted call.

When a summary exists​

The ordinary Stasis-end cleanup and resumable-fallback cleanup attempt CDR emission before tearing down available channel state. External-media leg termination does not produce its own CDR. A successfully paused resumable call does not emit at that pause. Missing channel state or a builder/logger error can leave no summary; emission failure is caught so cleanup can continue. This path is SIP call-end reporting, not evidence that every text or browser action produces a CDR.

The builder reads in-memory channel, flow and usage data. Although its interface allows a conversation-row fallback, these emission sites pass conversation: null; a missing metadata field is not automatically reloaded from Postgres. Investigate missing context at the producer instead of treating absent fields as empty values with business meaning.

Status and severity are different​

The logger emits the CDR at INFO severity even when its payload says status: error. The raw domain level is info; canonical telemetry severity remains uppercase as described above. Filtering only ERROR logs misses these summaries.

Status uses the following precedence:

  1. error — the caller media connection failed. When the media-connection monitor ended the call (termination reason Media), the CDR sets status: error, hangup.category: error and failure.failureCode to no_media or media_connection_broken, regardless of the conditions below.
  2. error — finalization reports an error. At these emission sites, the flag is set when flow finalization returns false. That can include unavailable or already-finalized state, not just a failed conversation.
  3. transferred — the last node exit is transfer or transfer_completed. This identifies the recorded flow exit; verify the actual transfer outcome separately.
  4. abandoned — the channel ended during pending setup. This applies only when no higher-precedence condition matches.
  5. completed — the default. This technical classification does not prove that the caller's task succeeded.

Apart from a media-connection failure, failure.failureCode prefers the most recent node-history structured error code, then the last exit reason, then the string hangup cause, otherwise null. It can therefore contain a non-error exit label. failure.errorType becomes FlowEngineError when the last node has an error message; it does not classify every network/provider failure.

The hangup category recognizes string causes NORMAL_CLEARING/16, BUSY/17, NO_ANSWER/19 and values beginning TRANSFER; other supplied strings map to error. The builder does not coerce a numeric hangup-cause value into a string, so a missing category is not evidence of normal clearing. Use SIP termination when you need the signaling cause rather than this summary classification.

Read the payload​

FieldsSource and limits
channelId, conversationId, callIdAvailable channel/application identities. Keep them distinct; a SIP call identifier is not automatically the database conversation ID.
sessionIdSet from channelData.conversationId in this builder. Do not assume it is an independently sourced browser session identity.
traceId, parentSpanIdActive span context when available; the builder can fall back to channel trace data. Direct telemetry treats trace correlation separately and strips the payload's camel-case trace alias.
appId, teamId, flowDefinitionId, flowVersionAvailable channel/call metadata. Missing runtime metadata remains missing at these emission sites.
startedAt, endedAt, durationSecondsEpoch-millisecond channel start and emission time; duration is their difference divided by 1,000. It is not a separately measured billed-media duration.
callerNumber, calledNumberChannel caller value and dialplan extension before logging redaction. They can contain personal information.
botName, botDisplayName, flowName, flowVersionLabelOptional supplied metadata; display name falls back to bot name. Labels are not substitutes for immutable app/flow identities.
botOutcomeOptional botOperationResult (success/failure), arbitrary botOperationData, last node exit and last-node error code. An absent result is not an implicit success.
providerAvailable unified provider/model or modular STT/LLM/TTS fields, fallback-chain indices and cache backend at hangup. It is a snapshot, not a complete provider-switch history.
hasRecordingBoolean presence of the call-recorder object at build time. It does not confirm successful processing, upload, retention or playback. Verify through recording diagnosis.
latencyDuration and available usage rollups described below. The name does not mean every latency metric is measured.

When a usage snapshot exists, latency can include LLM input/output/total tokens, per-provider TTS requests/chars, and per-model LLM requests/input/output tokens. Here requests comes from the recorded node count; do not equate it with provider HTTP attempts or an invoice.

rtpMediaSeconds is a best-effort estimate from sent RTP byte counts at an assumed 64 kbit/s. This implementation reads it only after a usage snapshot exists; absent usage can suppress it even when media counters exist. It is not a codec-independent duration measurement. Optional type fields such as aggregate/provider totalLatencyMs or TTS bytes are not populated by this builder; do not promise those columns or treat their absence as zero latency.

Find and verify the record​

  1. Select TelPhi, a bounded time range around call end, and logType = cdr in your log viewer. Add a known conversation or channel identifier; include INFO records.
  2. Inspect a real record's attributes before adding nested filters. Top-level fields such as status are separate from nested failure, provider, botOutcome and latency. The direct exporter does not universally flatten every nested CDR field; collector/index representation can differ.
  3. Compare the summary with conversation evidence, flow outcome and recording state. Do not diagnose a failed call from status alone.
  4. If the expected summary is absent, check the actual end/paused branch, available channel context, CdrBuildError, logger output and export/persistence failures. Follow the delivery-path checklist before assuming the call never happened.

Database persistence additionally requires database logging and a configured working sink. A visible telemetry summary does not prove an AppLog row exists. Likewise, these emission sites do not send a CDR to an arbitrary external HTTP recipient.

The shared logger redacts recognized phone/content/credential fields before its output paths. ENABLE_PII_LOGGING=true changes personal-data redaction behavior; credential handling remains separate. Arbitrary business data is not automatically safe just because the record is a CDR. Review the effective configuration and actual fields before exporting a diagnostic example, and keep customer-specific integration extensions in their authorized reference.

Safe interpretation​

The shared logger applies redaction before its output paths. Changed records can include pii_redacted and redacted_fields_count. Redaction is not evidence that every possible payload, native log or capture is safe to share. Use approved access, retention and export procedures; preserve exact executable field names in their permitted references.

Return to Logs and diagnostics for audit, SIP, service and CDR references and the delivery-path checklist.

For the Access Logs query, external collector selection and separate retention policies, use Verify security event export and retention.