Version: Next

Observability

Overview​

inPoint.Cortex is instrumented for observability on the three OpenTelemetry signals — logs, traces and metrics. The services and background workers that make up inPoint.Cortex emit this telemetry and export it over the OpenTelemetry Protocol (OTLP). When inPoint.Cortex is launched through its orchestrator the signals are collected by the built-in Aspire dashboard; in production they can be pointed at any OTLP-compatible collector or backend.

OpenTelemetry is already in use across several areas of inPoint.Cortex — the identity server, the web server, the database migration, and the archive jobs — and coverage is being extended to further components (for example core and hybrid store) over time.

This chapter is split in two parts:

  • Logging — how log entries are produced, enriched and exported, and how exceptions are recorded.
  • OpenTelemetry — the traces/spans each component emits and the attributes they carry, the full catalogue of metrics, and how the OTLP exporter is configured.

All telemetry export is opt-in and driven entirely by configuration: unless an OTLP endpoint is configured, each component still runs and logs locally but exports nothing.


Logging​

How logs are produced​

Every inPoint.Cortex component writes structured log entries. Log output always goes to the component's console/log file, and — when an OTLP endpoint is configured (see OTLP exporter configuration) — is additionally exported to the configured collector (the Aspire dashboard by default).

The default minimum log level and any per-category overrides are controlled from each component's appsettings*.json logging section, so administrators can raise or lower verbosity without code changes.

Contextual enrichment​

Logs are enriched with contextual properties so that related entries can be correlated. In particular, every entry written while a background job runs carries the job's identity:

PropertyMeaning
Job.IdThe job instance id.
Job.NameThe job short name.

These properties are attached automatically to all entries emitted during a job run, including those from the hosted job itself.

How exceptions are logged​

Exceptions are captured as structured data: the exception type, message and full stack trace are recorded alongside the log message rather than flattened into text. The same failure is also recorded on the active trace span (see Exceptions on spans) and, for background jobs, reflected in the job metrics via the result/error tags, so a single failure is visible in logs, traces and metrics.


OpenTelemetry​

Tracing​

Traces follow a request or a job through the system as a tree of spans. Where work crosses a process boundary (for example when a service starts a background worker), the trace context is propagated so that the whole operation appears as one connected distributed trace.

The custom spans are grouped below by the component that produces them. In addition to these, each web-facing component automatically produces standard spans for incoming HTTP requests and outgoing HTTP calls.

Identity server (inPoint.Web.IdentityServer)​

Spans covering authentication and token issuance.

SpanEmitted when
identity.login.passwordA username/password sign-in is attempted.
identity.login.signinA user is signed in.
identity.login.cookie_signinThe sign-in cookie is issued.
identity.validate_passwordA user's password is validated.
identity.validate_domain_credentialsDomain (directory) credentials are validated.
identity.mfa.enable_verifyA multi-factor enrolment code is verified.
identity.mfa.login_verifyA multi-factor login code is verified.
identity.external_login.callbackAn external identity provider callback is handled.
identity.token.get_profile_dataProfile/claims data is assembled for a token.
identity.token.is_activeA token's active state is checked.

Attributes seen on these spans include: identity.user_id, identity.username, identity.login_method, identity.identity_provider, identity.login_provider, identity.login_outcome, identity.password_valid, identity.mfa_code_valid, identity.domain, identity.account, identity.remember_login, identity.machine_name, identity.external_provider, identity.provider_user_id, identity.client_id, identity.is_active, identity.issued_claims_count.

In addition to the custom spans above, the identity server subscribes to the activity sources published by the underlying Open.IdentityServer engine, giving fine-grained visibility into protocol request processing. Each span is named after the class and method performing the operation (for example TokenEndpoint.ProcessAsync or ClientSecretValidator.ValidateAsync). The activity sources are:

Activity sourceCovers
Open.IdentityServerTop-level protocol request processing (token, authorize, etc.).
Open.IdentityServer.CacheCaching operations (reads, writes, invalidations).
Open.IdentityServer.ServicesInternal service operations (token creation, consent, key material).
Open.IdentityServer.StoresData store operations (reading/writing grants, clients, resources).
Open.IdentityServer.ValidationValidation operations (token, request and secret validation).

Web server (inPoint.Web.Server)​

The web server produces the standard incoming-request and outgoing-call spans. Server spans for the same route are given a readable, per-operation name, and calls to the enterprise back end are tagged with the specific operation being invoked so that otherwise identical requests can be told apart.

Database migration (inPoint.Setup.DbMigration)​

Spans covering database provisioning and script execution during setup/upgrade.

SpanEmitted when
database-migrationA migration run starts.
provision-databaseThe database is provisioned.
create-archiveAn archive is created.
provision-hybridstoreThe hybrid store is provisioned.
write-ldap-settingsDirectory (LDAP) settings are written.
run-database-scriptsThe database script phase starts.
execute-script <file>An individual database script is executed.
pco-import / run-pco-loaderA PCO import / loader run is executed.

Attributes seen on these spans include: migration.schema, migration.db_type, migration.db_version, migration.phase, migration.skipped, db.script.type, db.script.number, db.script.filename, and the PCO loader's pco.loader.config / pco.loader.exit_code.

Archive jobs (inPoint.Jobs)​

Jobs run across two processes: the archive service schedules and spawns jobs, and an out-of-process worker executes them. The trace context is propagated to the worker so both sides form a single distributed trace.

SpanProcessEmitted when
provision-jobsServiceA provisioning run starts (discover + create).
provision-jobServiceOnce per job being provisioned.
spawn-workerServiceA worker process is spawned and connected.
run-jobServiceOnce per scheduled run of a remote job.
execute-jobWorkerThe worker executes the hosted job.

Attributes seen on these spans include: job.id, job.name, job.class, job.assembly_file, job.provisioned, job.skip_reason, job.remote, job.finished, worker.framework, worker.pipe, and jobs.app_base. Individual jobs may add their own nested spans for the phases of work they perform.

Exceptions on spans​

When an operation fails, the failure is recorded on its span: the span is marked with an error status and carries the exception type (exception.type) and message (exception.message). Successful operations are marked with an OK status.

Metrics​

Metrics are reported across the components listed above. The table below is the complete catalogue of the custom metrics currently emitted. (Each component also reports standard runtime, incoming-request and outgoing-call metrics automatically.)

MetricTypeUnitTagsDescription
inPoint.jobs.execution.durationHistogramsinPoint.jobs.name, inPoint.jobs.remote, inPoint.jobs.result, error.typeJob run duration. Its count is the number of executions (there is no separate execution counter).
inPoint.jobs.execution.activeUp/down counter{job}inPoint.jobs.name, inPoint.jobs.remoteJobs running right now.
inPoint.jobs.restartCounter{restart}inPoint.jobs.nameWorker restarts triggered after a job run failed (lost connection / exited process).
inPoint.auditsCounter{audit}inPoint.audit.source, inPoint.audit.actionAudit entries recorded, tagged by audit source and action (see Auditing). Recorded on the producer side as entries are enqueued.

error.type is present only when a run actually threw; it carries the exception's type name.

The identity server additionally subscribes to the Open.IdentityServer meter published by the underlying engine, which reports counters for token issuance and validation activity. These instruments are tagged with values such as result (success / error / internal_error), client, grant_type, endpoint, auth_method and error:

MetricTypeDescription
tokenservice.operationCounterGeneral token-service operation outcomes.
tokenservice.active_requestsCounterToken-service requests in flight.
tokenservice.token_issuedCounterTokens issued, tagged by grant type and client.
tokenservice.introspectionCounterToken introspection calls.
tokenservice.revocationCounterToken revocation calls.
tokenservice.client.secret_validationCounterClient secret validations.
tokenservice.client.config_validationCounterClient configuration validations.
tokenservice.api.secret_validationCounterAPI secret validations.
tokenservice.resourceowner_authenticationCounterResource-owner (password) authentications.
tokenservice.device_authenticationCounterDevice authentications.
tokenservice.backchannel_authenticationCounterBack-channel (CIBA) authentications.
tokenservice.pushed_authorization_requestCounterPushed authorization requests (PAR).
tokenservice.dynamic_identityprovider.validationCounterDynamic identity-provider validations.

Tag values​

  • inPoint.jobs.name — the job short name.
  • inPoint.jobs.remote — true when the job ran out-of-process (remote worker), false for an in-process run.
  • inPoint.jobs.result — succeeded, failed or cancelled.

Reading the numbers​

  • There is no separate execution counter: the number of executions is the count of inPoint.jobs.execution.duration. Group it by inPoint.jobs.result for per-result totals.
  • inPoint.jobs.execution.active goes up when a run starts and down when it ends; it carries only the job name and remote flag.

OTLP exporter configuration​

Telemetry export is controlled entirely through environment variables / configuration. The presence of an OTLP endpoint is the switch that turns exporting on: when no endpoint is configured, each component runs normally and logs locally but exports nothing.

Default configuration in inPoint.Cortex (Aspire dashboard)​

When inPoint.Cortex is launched through its orchestrator, the Aspire dashboard is started automatically and the required OpenTelemetry environment variables are injected into each component. The relevant variables are:

VariablePurpose
OTEL_EXPORTER_OTLP_ENDPOINTThe OTLP collector endpoint (the Aspire dashboard). Its presence turns exporting on.
OTEL_EXPORTER_OTLP_HEADERSAny headers required by the collector (for example an API key for the dashboard).
OTEL_EXPORTER_OTLP_PROTOCOLThe OTLP protocol to use.
OTEL_SERVICE_NAMEThe logical component name shown in the dashboard.

No manual configuration is required for the default inPoint.Cortex experience — logs, traces and metrics from all instrumented components appear in the Aspire dashboard out of the box.

Configuring a custom OTLP target​

To send telemetry to your own collector or observability backend instead of (or in addition to) the Aspire dashboard, set the standard OpenTelemetry environment variables on each component, for example:

OTEL_EXPORTER_OTLP_ENDPOINT=https://otel-collector.example.com:4317
OTEL_EXPORTER_OTLP_PROTOCOL=grpc
OTEL_EXPORTER_OTLP_HEADERS=<header-name>=<value>
OTEL_SERVICE_NAME=<component name>

Because export is gated purely on OTEL_EXPORTER_OTLP_ENDPOINT, pointing that variable at a different host is all that is required to redirect the telemetry; the same variables apply to all three signals (logs, traces and metrics).