Coject.Core.Logging 1.1.0

dotnet add package Coject.Core.Logging --version 1.1.0
                    
NuGet\Install-Package Coject.Core.Logging -Version 1.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Coject.Core.Logging" Version="1.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Coject.Core.Logging" Version="1.1.0" />
                    
Directory.Packages.props
<PackageReference Include="Coject.Core.Logging" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Coject.Core.Logging --version 1.1.0
                    
#r "nuget: Coject.Core.Logging, 1.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Coject.Core.Logging@1.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Coject.Core.Logging&version=1.1.0
                    
Install as a Cake Addin
#tool nuget:?package=Coject.Core.Logging&version=1.1.0
                    
Install as a Cake Tool

Coject Core Logging

Coject.Core.Logging is the provider-neutral runtime pipeline for Coject Contracts 3.0 logging and auditing. It accepts typed records, captures trusted server context, redacts sensitive values before ownership, writes application and audit JSONL locally, and optionally fans records out to a provider registered by another package. Sprint 01 adds a validated, configurable storage layout without changing the Contracts 3.0 records or the existing durability and ownership guarantees.

This package owns the Core pipeline. The optional CSCC HTTP destination is in Coject.Core.Logging.Cscc. The package does not depend on CojectCore.Controller and does not register MVC controllers, routes, authentication, or database access.

Package and support

  • Package: Coject.Core.Logging version 1.0.0
  • Target framework: net8.0
  • Wire contract: Coject Contracts 3.0 from Coject.Core.Logging.Contracts 3.0.0
  • Repository: https://github.com/coject/CojectCore
  • The package contains portable symbols (.snupkg) and the packaged README.md, license, and icon.

The Sprint 01 storage contract is documented here as an additive configuration surface. This documentation change does not change project/package version fields, publish a package, upgrade a consumer, or deploy an application. Package publication and application deployment are separate approvals.

Installation

<PackageReference Include="Coject.Core.Logging" Version="1.0.0" />

The package declares a dependency on Coject.Core.Logging.Contracts 3.0.0 plus the required Microsoft.Extensions abstraction packages. Add Coject.Core.Logging.Cscc separately when CSCC delivery is required.

Registration

The normal host-level boundary is AddCojectLogging. It is idempotent and registers the typed logger, local application and audit providers, durable fallback, health services, and coordinated shutdown.

using Coject.Core.Logging;

var builder = Host.CreateApplicationBuilder(args);
builder.AddCojectLogging();

using var host = builder.Build();
await host.RunAsync();

For an ASP.NET Core application:

using Coject.Core.Logging;

var builder = WebApplication.CreateBuilder(args);
builder.AddCojectLogging();

var app = builder.Build();
app.MapGet("/", () => Results.Ok("running"));
app.Run();

AddCojectLogging() replaces existing ILoggerProvider registrations by default so framework records have one ownership path. During a deliberate coexistence migration only, use:

builder.AddCojectLogging(options =>
    options.ReplaceExistingFrameworkProviders = false);

AddCojectLoggingConfiguration(IServiceCollection, IConfiguration) is the lower-level boundary for hosts that need to compose registration manually. Prefer the host-builder method unless the application owns that composition explicitly.

Complete appsettings.json tree

The following is the complete bindable CojectLogging graph. CSCC is disabled in this safe example, so endpoint, key, and scope values are omitted.

{
  "CojectLogging": {
    "Environment": "Production",
    "TrustedContext": {
      "KnownProxies": [],
      "KnownNetworks": [],
      "ForwardLimit": 1,
      "PresentationTimeZoneId": "Asia/Riyadh"
    },
    "Local": {
      "RootPath": "logs",
      "Application": {
        "Enabled": true,
        "RetentionDays": 30,
        "MaxSegmentSizeBytes": 104857600,
        "BatchSize": 100,
        "FlushIntervalMilliseconds": 1000
      },
      "Audit": {
        "Enabled": true,
        "RetentionDays": 365,
        "MaxSegmentSizeBytes": 104857600,
        "BatchSize": 20,
        "FlushIntervalMilliseconds": 500
      },
      "Storage": {
        "Instance": {
          "Mode": "Generated",
          "Id": null,
          "ConflictPolicy": "Fail"
        },
        "Paths": {
          "InstanceRoot": "{root}/{service}/{environment}/{instance}",
          "LocalRoot": "{instanceRoot}/local",
          "ApplicationRoot": "{localRoot}/application",
          "AuditRoot": "{localRoot}/audit",
          "QuarantineRoot": "{laneRoot}/quarantine",
          "SpoolRoot": "{instanceRoot}/spool",
          "LogSpoolRoot": "{spoolRoot}/logs",
          "AuditSpoolRoot": "{spoolRoot}/audits",
          "ProviderOutboxRoot": "{root}/outbox",
          "OutboxQuarantineRoot": "{outboxLaneRoot}/quarantine",
          "OutboxArchiveRoot": "{outboxLaneRoot}/archive"
        },
        "Files": {
          "ApplicationSegment": "application-{service}-{environment}-{instance}-{segment}.jsonl",
          "AuditSegment": "audit-{service}-{environment}-{instance}-{segment}.jsonl",
          "WriterLock": ".writer.lock",
          "InstanceOwnershipLock": ".ownership.lock",
          "OutboxMetadata": "outbox-metadata.json"
        }
      }
    },
    "Cscc": {
      "Enabled": false
    },
    "Queues": {
      "Memory": {
        "Logs": {
          "MaxRecords": 10000,
          "MaxBytes": 134217728
        },
        "Audits": {
          "MaxRecords": 1000,
          "MaxBytes": 268435456
        }
      },
      "Overflow": {
        "Logs": {
          "MaxRecords": 25000,
          "MaxBytes": 536870912
        },
        "Audits": {
          "MaxRecords": 5000,
          "MaxBytes": 1073741824
        },
        "HighSeverityReservationPercent": 25
      },
      "HighSeverityBurstLimit": 8,
      "EnqueueTimeoutMs": 25,
      "HandoffRetryInitialDelaySeconds": 1,
      "HandoffRetryMaxDelaySeconds": 60,
      "ShutdownDrainTimeoutSeconds": 10
    },
    "Outbox": {
      "Retry": {
        "InitialDelaySeconds": 2,
        "Multiplier": 2,
        "MaxDelaySeconds": 300,
        "RetryAfterMinimumSeconds": 1,
        "RetryAfterMaximumSeconds": 3600
      },
      "Logs": {
        "MaxRecords": 100000,
        "MaxBytes": 2147483648,
        "HighSeverityReservationPercent": 25,
        "QuarantineRetentionDays": 90
      },
      "Audits": {
        "MaxRecords": 20000,
        "MaxBytes": 4294967296,
        "HighSeverityReservationPercent": 25,
        "QuarantineRetentionDays": 365
      }
    },
    "Redaction": {
      "AdditionalSensitiveFields": []
    },
    "Health": {
      "EmergencyDiagnosticMinimumIntervalSeconds": 60,
      "Disk": {
        "WarningFreeSpacePercent": 10,
        "WarningFreeSpaceBytes": 2147483648,
        "CriticalFreeSpacePercent": 5,
        "CriticalFreeSpaceBytes": 536870912,
        "CheckIntervalSeconds": 30
      }
    },
    "Framework": {
      "Enabled": true,
      "MinimumLevel": "Information"
    }
  }
}

Property behavior and limits

  • Environment defaults to Production; it must contain 1–64 printable characters.
  • TrustedContext.KnownProxies contains canonical proxy IPs; KnownNetworks contains aligned CIDR networks. Each list has at most 100 unique entries. ForwardLimit is 1–8. PresentationTimeZoneId defaults to Asia/Riyadh and is display-only.
  • Local.RootPath is required, is resolved relative to the process working directory when relative, and cannot contain control characters, wildcards, or a .. path segment. Application and audit JSONL sinks are mandatory in the configuration binding boundary. In this release, a missing or Enabled: false sink binding is normalized to its enabled default; keep both values true and control global disablement by not registering the package rather than relying on a local sink flag.
  • Local RetentionDays is 1–3650, MaxSegmentSizeBytes is 1–1073741824, BatchSize is 1–1000, and FlushIntervalMilliseconds is 50–5000.
  • Cscc.Enabled controls only the optional provider destination. When false, Core remains local-only and does not require valid endpoint, key, or scopes. When true, AppId is a non-empty identifier of at most 128 characters, Endpoint is an absolute HTTPS URI without user information, ApiKey is one scalar value of 32–512 non-whitespace characters, ContractVersion is exactly 3.0, and scopes are unique supported values containing logging.write. Supported scopes are logging.write and audit.write.
  • Cscc.Delivery.LogConcurrency and AuditConcurrency are 1–32 with a combined maximum of 64. AttemptTimeoutSeconds is 1–60.
  • Cscc.CircuitBreaker.FailureThreshold is positive; cooldown and rate-limit values must be positive finite numbers, maximum cooldown must not be below initial cooldown, maximum rate-limit pause must not be below minimum, and JitterRatio is greater than zero and at most 1.
  • Memory queue capacities are 1–1000000 records and 1–4294967296 bytes. HighSeverityBurstLimit is 1–100, EnqueueTimeoutMs is 1–250, handoff retry delays are 1–60 and 1–300 seconds with maximum not below initial, and shutdown drain is 1–30 seconds.
  • Queues.Overflow.HighSeverityReservationPercent is fixed at 25 in v1. Outbox lane reservation percentages are also fixed at 25.
  • Outbox capacities are 1–10000000 records and 1–17179869184 bytes. Quarantine retention is 1–3650 days. Retry delays and Retry-After bounds are positive finite values no greater than 3600 seconds; multiplier is 1–10; maximums cannot be below minimums.
  • Redaction.AdditionalSensitiveFields accepts at most 100 non-empty printable field names, each at most 128 characters. Names are normalized for matching.
  • Health diagnostic interval is 1–3600 seconds. Disk warning/critical percentages are positive and at most 100, critical must be below warning, byte thresholds are positive with critical below warning, and the disk check interval is 5–300 seconds.
  • Framework.Enabled controls the compatibility ILoggerProvider adapter only. Framework.MinimumLevel must be a defined Microsoft.Extensions.Logging level and does not filter explicit ICojectLogger calls.

Sprint 01 configurable storage contract

The Local.Storage section is additive. Omitting it produces the exact existing layout and names; Local.RootPath remains the compatibility alias for {root}. The resolver is shared by local application/audit providers, Core spool, provider outbox, retention, health, diagnostics, and recovery so those components cannot drift to different paths.

Instance identity and ownership

Mode Contract
Generated Default. Core generates a new safe instance value for every process start. Existing per-process isolation and restart behavior remain unchanged.
Stable Explicit operator choice. A safe non-empty Instance.Id is required. Restarts using the same root, service, environment, and ID reopen the same instance and continue its valid segments.

Stable is an exclusive single-owner mode. The local lanes and durable storage acquire their ownership locks before opening, repairing, rotating, cleaning, or recovering files. If another process owns the stable instance, ConflictPolicy: "Fail" (the default) returns a bounded failure and ConflictPolicy: "Defer" returns a bounded deferred disposition. Both policies leave the owned storage untouched: the process does not append, truncate, repair, rotate, or clean it, and it never silently falls back to a generated ID. Safe health/startup reporting uses the path-free codes STABLE_OWNERSHIP_CONFLICT or STABLE_OWNERSHIP_CONFLICT_DEFERRED. A stable instance that is currently owned is not treated as a released predecessor by previous-instance recovery.

The operator ID is an identity component, not a path. It must pass the same safe component validation as other resolved identities; do not use a path, separator, wildcard, control character, or .. segment as an ID.

Closed template grammar

Only these case-sensitive tokens are valid:

Token Meaning
{root} The configured Local.RootPath.
{service} The sanitized service identity.
{environment} The sanitized configured environment.
{instance} The generated or explicit stable instance identity.
{instanceRoot} The resolved service/environment/instance root.
{localRoot} The resolved local root.
{spoolRoot} The resolved Core spool root.
{laneRoot} The root of the local lane currently being resolved.
{outboxLaneRoot} The resolved provider/record-type outbox lane.
{segment} The library-controlled monotonically increasing segment number.

Path templates resolve in dependency order and must stay inside the approved root. They reject unknown tokens, malformed or cyclic references, absolute child segments, .., wildcards, control characters, empty required values, root escapes, collisions, and symlink/reparse-point paths. The closed contract keeps provider outbox storage under {root}/outbox; an external outbox root is not part of this recommendation.

File-name templates are names, not paths. They reject directory separators, wildcards, control characters, reserved device names, and unknown tokens. ApplicationSegment and AuditSegment must each contain exactly one {segment} token. The segment number is library-owned and remains an eight digit sequence (00000001, 00000002, ...). Ready, inflight, temporary, quarantine, archive, reason, checksum, metadata, and ownership lifecycle semantics remain library-owned even when an allowed name is configured.

Resulting layout

With RootPath: "logs", service SampleService, environment Production, and a generated instance generated-01, the default-compatible layout is:

<working-directory>/logs/SampleService/Production/generated-01/
  local/application/
    application-SampleService-Production-generated-01-00000001.jsonl
    .writer.lock
    quarantine/
  local/audit/
    audit-SampleService-Production-generated-01-00000001.jsonl
    .writer.lock
    quarantine/
  spool/
    .ownership.lock
    logs/
    audits/
    quarantine/logs/
    quarantine/audits/
<working-directory>/logs/outbox/
  outbox-metadata.json
  <destination>/logs/
    <entry>.ready | <entry>.inflight
    quarantine/<entry>.quarantine
    archive/<entry>.archive
  <destination>/audits/
    <entry>.ready | <entry>.inflight
    quarantine/<entry>.quarantine
    archive/<entry>.archive

<destination> is a validated provider component; the CSCC adapter uses Cscc. The outbox keeps logs and audits isolated by destination and record type. The internal files shown above are patterns, not application data to edit manually.

For a fixed application/audit root across restarts, use stable mode with an explicit operator ID and keep the instance component in the path. This is the recommended single-deployment example:

{
  "CojectLogging": {
    "Local": {
      "RootPath": "logs",
      "Storage": {
        "Instance": {
          "Mode": "Stable",
          "Id": "primary",
          "ConflictPolicy": "Fail"
        },
        "Paths": {
          "InstanceRoot": "{root}/{service}/{environment}/{instance}",
          "LocalRoot": "{instanceRoot}/local",
          "ApplicationRoot": "{localRoot}/application",
          "AuditRoot": "{localRoot}/audit",
          "QuarantineRoot": "{laneRoot}/quarantine",
          "SpoolRoot": "{instanceRoot}/spool",
          "LogSpoolRoot": "{spoolRoot}/logs",
          "AuditSpoolRoot": "{spoolRoot}/audits",
          "ProviderOutboxRoot": "{root}/outbox",
          "OutboxQuarantineRoot": "{outboxLaneRoot}/quarantine",
          "OutboxArchiveRoot": "{outboxLaneRoot}/archive"
        },
        "Files": {
          "ApplicationSegment": "application-{service}-{environment}-{instance}-{segment}.jsonl",
          "AuditSegment": "audit-{service}-{environment}-{instance}-{segment}.jsonl",
          "WriterLock": ".writer.lock",
          "InstanceOwnershipLock": ".ownership.lock",
          "OutboxMetadata": "outbox-metadata.json"
        }
      }
    }
  }
}

Its local roots are fixed at logs/<service>/<environment>/primary/local/application and logs/<service>/<environment>/primary/local/audit; a restart reopens the same highest valid segment. Do not remove {instance} while using Generated, because that would merge unrelated process histories and create a concurrent-writer conflict.

Rotation, retention, recovery, and quarantine

  • On startup, stable mode discovers the highest valid segment owned by the active template. It appends when that segment is below the configured size, rotates safely when full, and repairs only an incomplete or invalid tail. Valid records are preserved; the invalid tail is quarantined before appending.
  • Local retention matches only files owned by the active validated template, never the active segment or an unrelated file. Local invalid records and their bounded reason markers remain under the lane quarantine root until retention removes them.
  • Core spool writes durable artifacts through an atomic temporary-to-ready transition, ownership and capability checks, and checksum validation. It recovers only from configured managed roots; malformed, unsafe, or checksum failures are moved to type-specific quarantine with a safe reason.
  • Provider outbox entries preserve canonical redacted bytes and delivery metadata. Ready/inflight ownership, restart recovery, retry scheduling, permanent-failure quarantine, archive, and quarantine retention remain separate for each destination and record type. Archive and destructive lifecycle cleanup require the existing administrator authorization boundary.
  • A graceful shutdown closes intake, drains within the configured shutdown bound, and spills unresolved ownership. An abrupt stop leaves recoverable artifacts; the next eligible owner reconciles them without importing an active or locked stable instance.

CSCC being disabled does not disable local logging, local auditing, rotation, retention, quarantine, or Core spool recovery. With Cscc.Enabled: false, no CSCC HTTP clients or remote calls are created; the local lanes remain the source of record and provider outbox delivery is not attempted.

Migration and consumer handoff

Treat an instance-mode or template change as a storage migration. Core does not silently reinterpret old files under a new template, merge generated histories, rename existing directories, or delete old data.

  1. Record the current root, mode, ID (if stable), active segment numbers, and health state. Stop the application gracefully and verify that no process still owns the instance or its lanes.
  2. Take a recoverable backup of the complete managed root before changing a template or adopting an existing directory. Preserve the original tree until the new layout has been verified; restore rehearsal is recommended when the records are operationally important.
  3. Validate the new root/templates in a non-production or maintenance run. Confirm the closed token grammar, root containment, ownership markers, exact single {segment} token in each JSONL name, and collision-free application/audit/spool/outbox roots.
  4. For a layout-only cutover, start with the new template and let Core create a new stream. Keep the old tree read-only for historical access. This is the default safe procedure.
  5. For continuity, use Stable with an explicit safe operator ID and migrate only an intentionally selected compatible instance. Copy or move only validated, owned segments and durable artifacts while all writers are stopped; preserve canonical bytes and timestamps, resolve names to the new template, verify segment ordering and quarantine any invalid tail. Never copy an active or locked instance and never merge unrelated generated instances.
  6. Start once, confirm the highest valid segment is discovered and append or rotate behavior is correct, then check local application/audit files, spool/outbox health, quarantine counts, and shutdown/restart continuity. Retain the backup until this verification is complete.

Generated-to-stable migration is therefore explicit: leaving Generated keeps per-process isolation and starts a new instance on each process start; changing to Stable with a new ID starts a new stable instance; reusing an old directory is allowed only after the intentional backup, ownership, and compatibility checks above. A stable ownership conflict is resolved by stopping the competing owner or choosing a different reviewed ID; it is never resolved by automatic identity fallback.

The companion handoff note is handoff/phase-01/coject-core-logging-v1.01-s01-009-migration-handoff.md. Publishing the library package and deploying/upgrading an application remain separate approval decisions, even after the consumer configuration has been verified.

Typed logging and auditing

Resolve ICojectLogger from DI. The facade prepares immutable Contracts records, validates them, redacts them, and attempts bounded Core ownership. An accepted record is independent of the caller after the ownership result returns.

using Coject.Core.Logging.Contracts;
using Coject.Core.Logging.Contracts.Payloads;

var logger = host.Services.GetRequiredService<ICojectLogger>();
var result = await logger.LogAsync(
    CojectEvents.SystemExecution(new SystemExecutionPayload(stepName: "startup")),
    CojectLogLevel.Info,
    "Coject logging verification");

Console.WriteLine($"{result.Status}: {result.Reason}");

Audits use closed event/action factories from Contracts. A mutation audit must carry committed-state snapshots and a context provider must supply Module, Resource, and Operation.

using Coject.Core.Logging.Contracts;
using Coject.Core.Logging.Contracts.Payloads;

CojectAuditAction<DataCreationPayload> action =
    CojectAuditActions.DataCreation(
        new DataCreationPayload(newValues: persistedSnapshot));
var result = await logger.AuditAsync(action);

persistedSnapshot above is an application-created CojectSnapshot; do not pass request DTOs as a substitute for committed state. The Core audit validator rejects missing required context, incomplete snapshots, invalid event/action pairs, and no-change modifications.

Local JSONL and audit behavior

The local providers are always installed by Core. Each service instance gets separate application and audit lanes. With the default RootPath and no Local.Storage section, the layout is:

<working-directory>/logs/<service>/<environment>/<instance>/local/application/
  application-<service>-<environment>-<instance>-00000001.jsonl
<working-directory>/logs/<service>/<environment>/<instance>/local/audit/
  audit-<service>-<environment>-<instance>-00000001.jsonl

The service segment is derived from the entry assembly, environment is the configured value, and the instance segment is generated by Core. Each lane has a .writer.lock, rotates at MaxSegmentSizeBytes, batches writes, flushes on its interval, and removes files outside retention. Invalid managed records are isolated under that lane’s quarantine directory instead of being mixed into active JSONL.

Application records are operational/framework logs. Audit records are typed Contracts 3.0 records. Their canonical body is a nested envelope plus payload object; no provider-specific routing metadata is added to the body.

Failure, retry, and durable fallback

The enqueue path is intentionally bounded by Queues.EnqueueTimeoutMs. Memory capacity is independent for logs and audits. When a record cannot remain in memory, Core uses its protected overflow spool under the instance root:

<instance-root>/spool/logs/
<instance-root>/spool/audits/

The spool uses ownership and capability checks, preserves canonical bytes, recovers in the background, and quarantines malformed or unsafe entries under the resolved spool quarantine roots. Provider outbox state is separate from the Core spool and uses the resolved provider/record-type roots, with ready, inflight, quarantine, archive, metadata, and ownership lifecycle files. CSCC delivery (when installed) has independent log/audit queues, concurrency, retry, quota, and quarantine settings.

Transient transport failures, timeouts, HTTP 408/425/429, and HTTP 5xx responses are eligible for bounded retry. A valid Retry-After on HTTP 429 is honored within the configured limits. HTTP 401 and 403 are classified as authentication/scope failures. HTTP 400, 404, 405, 413, 415, 422, and other ordinary 4xx responses are permanent provider failures; they do not open the circuit and are not blindly retried. Uncertain outcomes such as an ingestion conflict are retained for reconciliation rather than treated as confirmed.

On shutdown, the coordinated shutdown service closes intake, drains within ShutdownDrainTimeoutSeconds, and spills unresolved ownership. Application code should still await host.StopAsync() during graceful shutdown.

Redaction and security

  • Never put API keys, bearer tokens, passwords, cookies, connection strings, or raw authorization headers in records or README/configuration committed to source control.
  • Contracts redaction runs before local persistence and provider handoff. Redaction.AdditionalSensitiveFields adds normalized field names to the built-in sensitive set; it does not disable built-in redaction.
  • Keep Cscc.ApiKey in a secret provider or environment-variable override. The configuration binder requires one scalar value and the validator rejects whitespace/control characters.
  • Use HTTPS endpoints only. The CSCC adapter sends the key in X-API-Key, sends X-Correlation-ID, and does not copy credentials into canonical records or health snapshots.
  • Keep KnownProxies and KnownNetworks narrow. Forwarded IP values are accepted only when rebuilt by a trusted edge and the connection matches a configured proxy or network.
  • System actors do not carry an actor IP. User and anonymous actors may carry a trusted normalized originating IP. Do not accept arbitrary client-supplied actor identity as trusted context.

Correlation, actor, and IP behavior

The capture boundary prefers a validated X-Correlation-ID, then an envelope correlation ID, and otherwise generates a safe correlation ID. If both header and envelope values exist they must match; valid IDs are 8–64 ASCII letters, digits, ., _, or -. Canonical occurrence time is UTC at millisecond precision. PresentationTimeZoneId affects display text only.

The trusted context model supports user, system, and anonymous actors. User/system actors require stable IDs; display names are bounded and sanitized. Request context fields are closed to correlationId, module, resource, operation, entityId, and statusCode. Business identifiers are bounded safe values, and status codes must be 100–599.

Minimal verification

  1. Start the host with CojectLogging configured and Cscc.Enabled set to false.
  2. Resolve ICojectLogger and run the SystemExecution example above.
  3. Confirm an application-*.jsonl file appears under the configured local path and contains a Contracts 3.0 record.
  4. For an audit integration, supply a trusted context provider with module/resource/operation, submit a closed CojectAuditActions action after the database commit, and confirm an audit-*.jsonl file appears.
  5. Check ICojectLoggingHealth for local queue, spool, disk, and lifecycle state rather than parsing credentials or provider response bodies.

Troubleshooting

Symptom Check
Host fails during startup Read the safe options validation message. Check HTTPS endpoint, scalar key shape, exact 3.0 contract version, queue bounds, and disk thresholds.
No local file Check the process working directory, safe Local.RootPath, write permissions, disk health, and whether the host was started long enough for the writer loop to flush.
Audit is rejected Confirm the action is from a closed Contracts factory, snapshots represent committed state, and the context contains module, resource, and operation.
Framework logs are missing Check Framework.Enabled and Framework.MinimumLevel; explicit typed records are independent of this filter.
CSCC is not called Install/register Coject.Core.Logging.Cscc, set Cscc.Enabled to true, and verify the validated endpoint and key are supplied through secrets.
CSCC returns 401/403 Verify the API key and the required logging.write/audit.write scope for the lane.
CSCC returns 422 Treat it as a permanent persistence-validation failure. Inspect the provider’s contract validation and the safe diagnostic code; do not add blind retries.
Files accumulate in spool/outbox Check provider reachability, circuit-breaker state, disk capacity, quota limits, and quarantine files. Reconcile only after confirming the provider’s acknowledgement semantics.

Controller dependency decision

CojectCore.Controller is intentionally not referenced. Core logging is a hosting/infrastructure library and its public contracts do not use Controller types. Applications that use both libraries may reference both independently; adding a Controller dependency here would expand the runtime graph without providing logging functionality.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Coject.Core.Logging:

Package Downloads
Coject.Core.Logging.Cscc

Optional CSCC destination adapter for the provider-neutral Coject Core Logging pipeline, with separate log and audit HTTP lanes, Contracts 3.0 transport, bounded delivery, retry, and circuit-breaker behavior.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.0 51 9/10/2026
1.0.0 148 8/15/2026

Initial publishable Coject Core Logging package. Includes the stable IHostApplicationBuilder.AddCojectLogging registration boundary, local JSONL application and audit sinks, durable fallback, and Contracts 3.0.0 support.