Elven.Observability.NotaCarioca.Logging.NLog 0.1.0-alpha.4

This is a prerelease version of Elven.Observability.NotaCarioca.Logging.NLog.
dotnet add package Elven.Observability.NotaCarioca.Logging.NLog --version 0.1.0-alpha.4
                    
NuGet\Install-Package Elven.Observability.NotaCarioca.Logging.NLog -Version 0.1.0-alpha.4
                    
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="Elven.Observability.NotaCarioca.Logging.NLog" Version="0.1.0-alpha.4" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Elven.Observability.NotaCarioca.Logging.NLog" Version="0.1.0-alpha.4" />
                    
Directory.Packages.props
<PackageReference Include="Elven.Observability.NotaCarioca.Logging.NLog" />
                    
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 Elven.Observability.NotaCarioca.Logging.NLog --version 0.1.0-alpha.4
                    
#r "nuget: Elven.Observability.NotaCarioca.Logging.NLog, 0.1.0-alpha.4"
                    
#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 Elven.Observability.NotaCarioca.Logging.NLog@0.1.0-alpha.4
                    
#: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=Elven.Observability.NotaCarioca.Logging.NLog&version=0.1.0-alpha.4&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Elven.Observability.NotaCarioca.Logging.NLog&version=0.1.0-alpha.4&prerelease
                    
Install as a Cake Tool

Elven Observability Nota Carioca .NET Framework 4.7.2/4.8

Português (Brasil)

Elven Observability Nota Carioca is a safe-default OpenTelemetry distribution for critical .NET Framework 4.7.2 and 4.8 applications.

It is designed for legacy enterprise systems where low risk, easy rollback, and predictable behavior matter more than magic.

The library exports telemetry to a customer-side OpenTelemetry Collector. Application code should not contain Elven backend credentials. Keep backend tokens and tenant routing in the Collector.

What You Get

  • Traces, metrics, and logs for .NET Framework 4.7.2 and 4.8.
  • W3C trace context where possible.
  • WCF client/server instrumentation through Elven behaviors and message inspectors.
  • ASP.NET classic HTTP module support, including ASMX/SOAP operation naming.
  • Outbound HTTP and SQL instrumentation through stable OpenTelemetry packages.
  • Oracle datastore spans through an explicit low-risk ADO.NET helper.
  • EF6 support through an explicit DbCommandInterceptor.
  • Manual business spans and business/product metrics.
  • log4net, NLog, Serilog, and System.Diagnostics bridges.
  • Redaction and masking before export.
  • Kill switches through app.config, web.config, or environment variables.

Supported Runtime

  • .NET Framework 4.7.2 and 4.8.
  • Windows Server, IIS, IIS Express, Windows Services, and console apps.
  • ASP.NET classic, ASP.NET MVC/Web API on .NET Framework, WCF, EF6, ADO.NET, and legacy logging frameworks.

This package is not for ASP.NET Core or modern .NET. Use the modern Elven .NET package for those applications.

Quick Start

Use this order for a first installation in a legacy app.

  1. Make sure an OpenTelemetry Collector is reachable from the application.
  2. Install the package or packages needed by the app.
  3. Configure appSettings or environment variables.
  4. Initialize Elven Observability once during application startup.
  5. Add WCF, ASP.NET, EF6, or logging bridge wiring only where needed.
  6. Add business metrics and spans for critical journeys.
  7. Run locally or in staging with the Collector offline and online.
  8. Keep rollback ready with ELVEN_ENABLED=false.

Packages

The meta-package is intentionally conservative. It includes the safe default surface for core bootstrap, ASP.NET classic/ASMX, HTTP, SQL, Oracle helper APIs, and business APIs. WCF, EF6, and logging bridges are installed explicitly so critical legacy apps do not pull unnecessary dependencies.

dotnet add package Elven.Observability.NotaCarioca

For older non-SDK-style .NET Framework projects, Visual Studio Package Manager Console is usually easier:

Install-Package Elven.Observability.NotaCarioca

You can also use the Visual Studio NuGet Package Manager UI. The important part is that the installed packages appear in the application project that owns the final app.config or web.config.

Install extra packages based on what the application actually uses:

Scenario Package
WCF client/server behaviors Elven.Observability.NotaCarioca.Wcf
Oracle ADO.NET helper Elven.Observability.NotaCarioca.Oracle
EF6 command interceptor Elven.Observability.NotaCarioca.EF6
log4net bridge Elven.Observability.NotaCarioca.Logging.Log4Net
NLog bridge Elven.Observability.NotaCarioca.Logging.NLog
Serilog bridge Elven.Observability.NotaCarioca.Logging.Serilog
System.Diagnostics Trace bridge Elven.Observability.NotaCarioca.Logging.SystemDiagnostics
Core-only custom integration Elven.Observability.NotaCarioca.Core
Business-only API reference Elven.Observability.NotaCarioca.Business

Typical WCF service:

dotnet add package Elven.Observability.NotaCarioca
dotnet add package Elven.Observability.NotaCarioca.Wcf
dotnet add package Elven.Observability.NotaCarioca.Logging.Log4Net

Typical ASP.NET MVC/Web API application:

dotnet add package Elven.Observability.NotaCarioca
dotnet add package Elven.Observability.NotaCarioca.EF6
dotnet add package Elven.Observability.NotaCarioca.Logging.NLog

Typical ASP.NET WebForms/ASMX application with Oracle:

Install-Package Elven.Observability.NotaCarioca -Version 0.1.0-alpha.4 -Prerelease
Install-Package Elven.Observability.NotaCarioca.Oracle -Version 0.1.0-alpha.4 -Prerelease
Install-Package Elven.Observability.NotaCarioca.Logging.SystemDiagnostics -Version 0.1.0-alpha.4 -Prerelease

Collector First

Configure the application to send OTLP to a local or customer-side Collector:

<appSettings>
  <add key="ELVEN_SERVICE_NAME" value="nota-carioca-nfse" />
  <add key="ELVEN_ENVIRONMENT" value="production" />
  <add key="OTEL_EXPORTER_OTLP_ENDPOINT" value="http://otel-collector.local:4318" />
  <add key="OTEL_EXPORTER_OTLP_PROTOCOL" value="http/protobuf" />
</appSettings>

The default protocol is http/protobuf on port 4318. This is the recommended default for legacy .NET Framework applications. gRPC is available only when explicitly configured and validated in the customer's runtime.

Collector reference config:

docs/collector.example.yaml

Initialization

Initialize once per AppDomain.

Do this before the first ElvenMetrics, ElvenTrace, EF6 registration, or logging bridge call. The manual facades have a lazy safety net, but production apps should initialize explicitly during startup so service name, environment, collector endpoint, and kill switches are resolved before telemetry begins.

For config-first applications, use Initialize() and keep values in app.config, web.config, or environment variables:

using Elven.Observability.NotaCarioca;

var observability = ElvenObservability.Initialize();

For config plus a small code override, use the callback overload. This reads app settings and env vars first, then applies the callback:

using Elven.Observability.NotaCarioca;

var observability = ElvenObservability.Initialize(options =>
{
    options.ServiceVersion = typeof(Program).Assembly.GetName().Version?.ToString();
    options.EnableWcfInstrumentation = true;
});

For full programmatic configuration, pass ElvenLegacyOptions directly:

using Elven.Observability.NotaCarioca;

var observability = ElvenObservability.Initialize(new ElvenLegacyOptions
{
    ServiceName = "nota-carioca-nfse",
    Environment = "production",
    CollectorEndpoint = new Uri("http://otel-collector.local:4318"),
    EnableWcfInstrumentation = true,
    EnableSqlInstrumentation = true,
    EnableLog4NetBridge = true
});

Important: external false kill switches in env vars or appSettings still win over programmatic true.

Dispose the handle during controlled shutdown. Dispose() already performs a timeout-bounded flush, so do not call ForceFlushAsync() immediately before it:

observability.Dispose();

Configuration Reference

Common keys:

Key Default Purpose
ELVEN_ENABLED true Global kill switch. Set false for emergency rollback.
ELVEN_SERVICE_NAME unknown-legacy-service Logical service name. Set this in every app.
ELVEN_SERVICE_NAMESPACE legacy Optional namespace for grouping services.
ELVEN_SERVICE_VERSION empty App version. Useful for release correlation.
ELVEN_ENVIRONMENT empty production, staging, qa, etc.
OTEL_EXPORTER_OTLP_ENDPOINT http://localhost:4318 Collector base endpoint. With http/protobuf, the library sends traces to /v1/traces, metrics to /v1/metrics, and logs to /v1/logs.
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT empty Optional traces-only endpoint. Use the full OTLP/HTTP path, for example http://localhost:4318/v1/traces.
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT empty Optional metrics-only endpoint. Use the full OTLP/HTTP path, for example http://localhost:4318/v1/metrics.
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT empty Optional logs-only endpoint. Use the full OTLP/HTTP path, for example http://localhost:4318/v1/logs.
OTEL_EXPORTER_OTLP_PROTOCOL http/protobuf Use http/protobuf unless gRPC is validated.
OTEL_EXPORTER_OTLP_TRACES_PROTOCOL empty Optional traces-only protocol override.
OTEL_EXPORTER_OTLP_METRICS_PROTOCOL empty Optional metrics-only protocol override.
OTEL_EXPORTER_OTLP_LOGS_PROTOCOL empty Optional logs-only protocol override.
OTEL_EXPORTER_OTLP_TIMEOUT 30000 Export timeout in milliseconds.
OTEL_EXPORTER_OTLP_TRACES_TIMEOUT empty Optional traces-only timeout in milliseconds.
OTEL_EXPORTER_OTLP_METRICS_TIMEOUT empty Optional metrics-only timeout in milliseconds.
OTEL_EXPORTER_OTLP_LOGS_TIMEOUT empty Optional logs-only timeout in milliseconds.
OTEL_METRIC_EXPORT_INTERVAL 60000 Metric export interval in milliseconds.
OTEL_TRACES_EXPORTER otlp Set none to disable trace export.
OTEL_METRICS_EXPORTER otlp Set none to disable metric export.
OTEL_LOGS_EXPORTER otlp Set none to disable log export.
OTEL_TRACES_SAMPLER always_on equivalent Supports always_on, always_off, and ratio through OTEL_TRACES_SAMPLER_ARG.
ELVEN_REDACTION_MODE default Use default or strict. off/none is only for controlled non-production tests. Hard secrets still stay redacted.
ELVEN_LOG_BODY_MODE redacted Controls log body handling independently from trace/log correlation. Values: redacted, metadata, off, passthrough.
ELVEN_ALLOWED_RAW_ATTRIBUTES empty Exact allowlist for raw attributes. Use sparingly. It never bypasses hard-secret redaction.
ELVEN_MARK_ACTIVE_SPAN_ON_ERROR_LOG false Opt-in: when a legacy log bridge emits Error or higher and an Activity is active, mark that span as error and add a sanitized log.error event.
ELVEN_MAX_METRIC_TAG_COUNT 12 Maximum tags per metric datapoint.
ELVEN_MAX_TAG_VALUE_LENGTH 256 Maximum tag value length.
ELVEN_MAX_STATEMENT_LENGTH 8192 Maximum exported db.statement / db.query.text length after redaction. Truncated statements end with [TRUNCATED].
ELVEN_MAX_LOG_BODY_LENGTH 65536 Maximum log body length emitted by Elven log bridges after redaction. Truncated bodies end with [TRUNCATED].

Module kill switches:

Key Default Purpose
ELVEN_ENABLE_ASPNET true ASP.NET classic HTTP module.
ELVEN_ENABLE_ASMX true ASMX/SOAP naming and metadata when the ASP.NET module sees .asmx requests.
ELVEN_ENABLE_HTTP true Outbound HTTP instrumentation.
ELVEN_ENABLE_SQL true SqlClient instrumentation.
ELVEN_ENABLE_ORACLE true Manual Oracle ADO.NET helper spans and metrics. Safe no-op unless the helper is called.
ELVEN_ENABLE_EF6 false EF6 interceptor support. Requires explicit registration.
ELVEN_ENABLE_WCF false WCF behavior/message inspector support.
ELVEN_ENABLE_RUNTIME_METRICS true Runtime metrics.
ELVEN_ENABLE_PROCESS_METRICS false Process gauges. Opt-in.
ELVEN_ENABLE_LOG4NET false log4net bridge.
ELVEN_ENABLE_NLOG false NLog bridge.
ELVEN_ENABLE_SERILOG false Serilog bridge.
ELVEN_ENABLE_SYSTEM_DIAGNOSTICS false System.Diagnostics TraceListener bridge.

Emergency rollback:

<appSettings>
  <add key="ELVEN_ENABLED" value="false" />
</appSettings>

Signal-specific rollback:

<appSettings>
  <add key="OTEL_TRACES_EXPORTER" value="none" />
  <add key="OTEL_METRICS_EXPORTER" value="none" />
  <add key="OTEL_LOGS_EXPORTER" value="none" />
</appSettings>

ASP.NET Classic

Install:

dotnet add package Elven.Observability.NotaCarioca

Add the module to web.config:

<configuration>
  <appSettings>
    <add key="ELVEN_SERVICE_NAME" value="legacy-web" />
    <add key="ELVEN_ENVIRONMENT" value="production" />
    <add key="OTEL_EXPORTER_OTLP_ENDPOINT" value="http://otel-collector.local:4318" />
    <add key="OTEL_EXPORTER_OTLP_PROTOCOL" value="http/protobuf" />
    <add key="ELVEN_ENABLE_ASPNET" value="true" />
    <add key="ELVEN_ENABLE_ASMX" value="true" />
  </appSettings>

  <system.web>
    <compilation targetFramework="4.7.2" />
    <httpRuntime targetFramework="4.7.2" />
    <httpModules>
      <add name="ElvenObservability"
           type="Elven.Observability.NotaCarioca.AspNet.ElvenAspNetHttpModule, Elven.Observability.NotaCarioca.AspNet" />
    </httpModules>
  </system.web>

  <system.webServer>
    <validation validateIntegratedModeConfiguration="false" />
    <modules runAllManagedModulesForAllRequests="true">
      <add name="ElvenObservability"
           type="Elven.Observability.NotaCarioca.AspNet.ElvenAspNetHttpModule, Elven.Observability.NotaCarioca.AspNet" />
    </modules>
  </system.webServer>
</configuration>

The module creates inbound HTTP server spans, records status codes, records sanitized exceptions, and sets correlation_id from x-correlation-id or x-request-id when present.

ASMX/SOAP

ASMX instrumentation is part of the ASP.NET classic module. It does not read SOAP bodies. It uses the request path, PathInfo, and SOAPAction header to produce useful low-cardinality server span names.

For a request like:

POST /WSNacional/nfse.asmx
SOAPAction: "http://notacarioca.rio.gov.br/GerarNfse"
traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
baggage: client=prefeitura-rio,journey=nfse

The server span is named:

ASMX nfse/GerarNfse

Common tags:

Attribute Example
rpc.system asmx
rpc.service nfse
rpc.method GerarNfse
soap.action http://notacarioca.rio.gov.br/GerarNfse
http.route /WSNacional/nfse.asmx/GerarNfse
http.response.status_code 200
correlation_id existing or generated correlation id

Inbound W3C traceparent, tracestate, and baggage are preserved so upstream services can continue the same distributed trace through the legacy ASMX app.

Manual Propagation For Legacy Threads And Web References

The ASP.NET module and supported HTTP instrumentation handle the common path. Some legacy code uses raw Thread, Timer, generated Web References, or custom HttpWebRequest creation where Activity.Current can be lost before the outbound call. In those cases, capture context before crossing the boundary and inject it explicitly.

Capture and restore context around background work:

var context = ElvenTrace.CaptureContext();

ThreadPool.QueueUserWorkItem(_ =>
{
    using (ElvenTrace.StartBusinessSpan("NotaNacional Worker", new
    {
        journey = "nota_nacional",
        phase = "api_fetch"
    }, context))
    {
        // legacy worker code
    }
});

VB.NET:

Dim context = ElvenTrace.CaptureContext()

Dim thread = New Threading.Thread(
    Sub()
        Using ElvenTrace.StartBusinessSpan(
            "NotaNacional Worker",
            New With {.journey = "nota_nacional", .phase = "api_fetch"},
            context)

            ' legacy worker code
        End Using
    End Sub)

thread.Start()

Inject W3C headers manually when a legacy outbound client bypasses automatic instrumentation:

var request = (HttpWebRequest)WebRequest.Create(url);
ElvenPropagation.InjectHttpHeaders(request.Headers);

This writes traceparent, tracestate, baggage, x-correlation-id, and x-request-id when values are available. Header injection is fail-open: if a legacy transport rejects a header, the application call still continues.

WCF

WCF is first-class, but opt-in by design. The main package does not install WCF behaviors automatically.

Install both packages in the WCF application project:

Install-Package Elven.Observability.NotaCarioca -Version 0.1.0-alpha.4 -Prerelease
Install-Package Elven.Observability.NotaCarioca.Wcf -Version 0.1.0-alpha.4 -Prerelease

For SDK-style projects:

dotnet add package Elven.Observability.NotaCarioca --version 0.1.0-alpha.4
dotnet add package Elven.Observability.NotaCarioca.Wcf --version 0.1.0-alpha.4

WCF Setup Checklist

  1. Install Elven.Observability.NotaCarioca.
  2. Install Elven.Observability.NotaCarioca.Wcf.
  3. Set service name, environment, Collector endpoint, and ELVEN_ENABLE_WCF=true.
  4. Initialize Elven Observability once during app startup.
  5. Register the WCF behavior extension.
  6. Attach the service behavior to WCF services and the endpoint behavior to WCF clients.
  7. Run a staging call and verify one WCF client span and one WCF server span.

WCF App Settings

Use this in web.config or app.config:

<appSettings>
  <add key="ELVEN_SERVICE_NAME" value="nota-carioca-nfse" />
  <add key="ELVEN_ENVIRONMENT" value="production" />
  <add key="OTEL_EXPORTER_OTLP_ENDPOINT" value="http://otel-collector.local:4318" />
  <add key="OTEL_EXPORTER_OTLP_PROTOCOL" value="http/protobuf" />
  <add key="ELVEN_ENABLE_WCF" value="true" />
</appSettings>

Emergency rollback:

<appSettings>
  <add key="ELVEN_ENABLED" value="false" />
</appSettings>

WCF-only rollback:

<appSettings>
  <add key="ELVEN_ENABLE_WCF" value="false" />
</appSettings>

WCF Startup Code

Initialize once before the first WCF call or before opening a ServiceHost.

ASP.NET/IIS-hosted .svc service, for example in Global.asax:

using System;
using System.Web;
using Elven.Observability.NotaCarioca;

public class Global : HttpApplication
{
    private static IElvenObservabilityHandle observability;

    protected void Application_Start(object sender, EventArgs e)
    {
        observability = ElvenObservability.Initialize();
    }

    protected void Application_End(object sender, EventArgs e)
    {
        if (observability != null)
        {
            observability.Dispose();
        }
    }
}

Windows Service or console-hosted WCF:

using System;
using System.ServiceModel;
using Elven.Observability.NotaCarioca;

public static class Program
{
    public static void Main()
    {
        var observability = ElvenObservability.Initialize();

        try
        {
            using (var host = new ServiceHost(typeof(NfseService)))
            {
                host.Open();
                Console.WriteLine("WCF service is running. Press ENTER to stop.");
                Console.ReadLine();
            }
        }
        finally
        {
            observability.Dispose();
        }
    }
}

The WCF inspectors also have a lazy safety net, but explicit startup initialization is the recommended production path.

Register The WCF Behavior Extension

Add this once under system.serviceModel:

<system.serviceModel>
  <extensions>
    <behaviorExtensions>
      <add name="elvenObservability"
           type="Elven.Observability.NotaCarioca.Wcf.ElvenWcfBehaviorExtensionElement, Elven.Observability.NotaCarioca.Wcf" />
    </behaviorExtensions>
  </extensions>

  <behaviors>
    <endpointBehaviors>
      <behavior name="observedEndpoint">
        <elvenObservability serviceName="nota-carioca-nfse" />
      </behavior>
    </endpointBehaviors>

    <serviceBehaviors>
      <behavior name="observedService">
        <elvenObservability serviceName="nota-carioca-nfse" />
      </behavior>
    </serviceBehaviors>
  </behaviors>
</system.serviceModel>

serviceName is optional, but recommended. Use the same logical service name that the client expects to see in dashboards, for example nota-carioca-nfse, nfse-web, or nota-carioca-worker.

WCF Server Service

For WCF services, attach behaviorConfiguration="observedService" to the service. The service behavior observes all endpoints hosted by that service:

<system.serviceModel>
  <services>
    <service name="GovRio.Nfse.NfseService"
             behaviorConfiguration="observedService">
      <endpoint address=""
                binding="basicHttpBinding"
                contract="GovRio.Nfse.INfseService" />
    </service>
  </services>
</system.serviceModel>

For services with multiple endpoints, keep the service behavior on the service and list the endpoints normally:

<endpoint address="basic"
          binding="basicHttpBinding"
          contract="GovRio.Nfse.INfseService" />

<endpoint address="tcp"
          binding="netTcpBinding"
          contract="GovRio.Nfse.INfseService" />

Avoid attaching both observedService and observedEndpoint to the same server endpoint. The library has a defensive duplicate-inspector guard, but the clean configuration is service behavior for servers and endpoint behavior for clients.

WCF Client Endpoint

Attach behaviorConfiguration="observedEndpoint" to the generated or hand-written WCF client endpoint:

<system.serviceModel>
  <client>
    <endpoint name="NfseService"
              address="http://nfse.local/NfseService.svc"
              binding="basicHttpBinding"
              contract="GovRio.Nfse.INfseService"
              behaviorConfiguration="observedEndpoint" />
  </client>
</system.serviceModel>

If the application has several external service endpoints, configure each one explicitly:

<client>
  <endpoint name="Amadeus"
            address="http://nfse-provider.local/NfseService.svc"
            binding="basicHttpBinding"
            contract="GovRio.Nfse.INfseService"
            behaviorConfiguration="observedEndpoint" />

  <endpoint name="Gol"
            address="net.tcp://prefeitura.local:808/NfseService"
            binding="netTcpBinding"
            contract="GovRio.Nfse.INfseService"
            behaviorConfiguration="observedEndpoint" />
</client>

What WCF Telemetry Includes

The WCF behavior records:

  • client spans for outbound WCF calls;
  • server spans for inbound WCF operations;
  • rpc.system=wcf;
  • service/contract information when available;
  • operation/action name;
  • binding name, such as BasicHttpBinding or NetTcpBinding;
  • endpoint/remote address when available;
  • SOAP Fault detection;
  • WCF duration metrics through rpc.server.duration, rpc.client.duration, and HTTP-compatible http.server.request.duration / http.client.request.duration;
  • WCF error category for SOAP Faults and abandoned calls;
  • exception type and sanitized message when the exception is recorded by a manual span, logging bridge, ASP.NET module, or surrounding application catch block;
  • correlation_id for cross-signal dashboards.

What WCF Telemetry Does Not Collect By Default

The library does not collect giant SOAP/XML bodies by default.

By default it does not export:

  • SOAP envelope body;
  • raw request XML;
  • raw response XML;
  • WS-Security credentials;
  • tokens, passwords, session ids, cookies, cards, CPF/CNPJ, e-mail, or phone values;
  • arbitrary application headers. Header allowlists are reserved for a future implementation and do not enable capture in this release.

This is intentional for legacy enterprise systems. It gives observability for calls, dependencies, faults, latency, and correlation without moving sensitive payloads into telemetry.

WCF Context Propagation

Outbound WCF clients inject:

  • traceparent
  • tracestate
  • baggage
  • x-correlation-id

Inbound WCF services extract:

  • traceparent
  • tracestate
  • baggage
  • x-correlation-id
  • x-request-id

Propagation is written to SOAP headers and to HTTP headers when the binding exposes HTTP transport headers. netTcpBinding still carries SOAP headers.

If a caller sends W3C context, Elven continues that trace. If no W3C context exists, Elven creates a trace and uses correlation_id as the fallback business/operations correlation key.

WCF Business Metrics Example

Automatic WCF spans show the technical call. Add business metrics around the operation to power journey dashboards:

using Elven.Observability.NotaCarioca;

public NfseResponse ConsultarNfse(NfseRequest request)
{
    var started = DateTimeOffset.UtcNow;

    using (var span = ElvenTrace.StartBusinessSpan("ConsultarNfse", new
    {
        journey = "consulta_nfse",
        provider = request.Provider,
        source = "nota-carioca",
        client_id = request.ClientId
    }))
    {
        try
        {
            var response = providerClient.ConsultarNfse(request);

            ElvenMetrics.Counter("nfse.consulta.requested")
                .Add(1, new
                {
                    journey = "consulta_nfse",
                    provider = request.Provider,
                    status = "success"
                });

            span.SetAttribute("result", "success");
            return response;
        }
        catch (TimeoutException ex)
        {
            span.RecordException(ex);

            ElvenMetrics.Counter("nfse.consulta.failed")
                .Add(1, new
                {
                    journey = "consulta_nfse",
                    provider = request.Provider,
                    error_category = "provider_error"
                });

            throw;
        }
        finally
        {
            ElvenMetrics.Duration("external_service.latency")
                .Record(DateTimeOffset.UtcNow - started, new
                {
                    journey = "consulta_nfse",
                    provider = request.Provider,
                    operation = "consulta_nfse"
                });
        }
    }
}

client_id is hashed by default before export. Sensitive values inside tags are redacted before they leave the process.

Manual WCF Fallback

If the automatic behavior does not fit a legacy binding, keep the package installed and use manual spans around critical operations:

using System;
using Elven.Observability.NotaCarioca.Wcf;

using (ElvenWcfManual.StartOperation("ConsultarNfse", new
{
    journey = "consulta_nfse",
    provider = "nota-carioca"
}))
{
    try
    {
        // WCF operation code
    }
    catch (Exception ex)
    {
        ElvenWcfManual.RecordException(ex);
        throw;
    }
}

WCF Validation Checklist

After installation, validate these items in staging:

  • the app starts with ELVEN_ENABLE_WCF=true;
  • the same app starts with ELVEN_ENABLE_WCF=false;
  • one successful WCF call produces a client span;
  • the server side produces a server span when the service is also instrumented;
  • traceparent is propagated between client and server;
  • correlation_id is present in traces and logs;
  • SOAP Faults are marked as errors;
  • no SOAP body or large XML payload appears in telemetry;
  • app pool recycle or service stop flushes telemetry when possible.

Common fixes:

  • If there are no WCF spans, confirm Elven.Observability.NotaCarioca.Wcf is installed in the executable/web app project.
  • Confirm ELVEN_ENABLE_WCF=true.
  • Confirm the behavior extension is registered under system.serviceModel/extensions/behaviorExtensions.
  • For server-side spans, confirm behaviorConfiguration="observedService" is attached to the <service>.
  • For client-side spans, confirm behaviorConfiguration="observedEndpoint" is attached to the exact <client> endpoint used by the application.
  • For generated service references, edit the generated client endpoint in app.config or web.config, not only the service contract.
  • For HTTPS, WS-Security, custom bindings, and certificate-heavy scenarios, validate in staging with customer-like bindings before broad rollout.

Complete Minimal WCF Example

This is the smallest complete shape most WCF applications need. Keep the services section for a WCF server, keep the client section for a WCF client, or keep both when the same process acts as both.

<configuration>
  <appSettings>
    <add key="ELVEN_SERVICE_NAME" value="nota-carioca-nfse" />
    <add key="ELVEN_ENVIRONMENT" value="production" />
    <add key="OTEL_EXPORTER_OTLP_ENDPOINT" value="http://otel-collector.local:4318" />
    <add key="OTEL_EXPORTER_OTLP_PROTOCOL" value="http/protobuf" />
    <add key="ELVEN_ENABLE_WCF" value="true" />
  </appSettings>

  <system.serviceModel>
    <extensions>
      <behaviorExtensions>
        <add name="elvenObservability"
             type="Elven.Observability.NotaCarioca.Wcf.ElvenWcfBehaviorExtensionElement, Elven.Observability.NotaCarioca.Wcf" />
      </behaviorExtensions>
    </extensions>

    <behaviors>
      <endpointBehaviors>
        <behavior name="observedEndpoint">
          <elvenObservability serviceName="nota-carioca-nfse" />
        </behavior>
      </endpointBehaviors>

      <serviceBehaviors>
        <behavior name="observedService">
          <elvenObservability serviceName="nota-carioca-nfse" />
        </behavior>
      </serviceBehaviors>
    </behaviors>

    <services>
      <service name="GovRio.Nfse.NfseService"
               behaviorConfiguration="observedService">
        <endpoint address=""
                  binding="basicHttpBinding"
                  contract="GovRio.Nfse.INfseService" />
      </service>
    </services>

    <client>
      <endpoint name="NfseService"
                address="http://nfse.local/NfseService.svc"
                binding="basicHttpBinding"
                contract="GovRio.Nfse.INfseService"
                behaviorConfiguration="observedEndpoint" />
    </client>
  </system.serviceModel>
</configuration>

Programmatic startup:

using Elven.Observability.NotaCarioca;

ElvenObservability.Initialize();

Validated WCF Coverage

The CI release gate validates WCF coverage for:

  • basicHttpBinding;
  • netTcpBinding;
  • W3C propagation;
  • correlation_id propagation;
  • client and server spans;
  • SOAP Fault error marking, exception classification, and metadata-only exception events in the isolated Windows GA smoke;
  • duplicate-inspector protection when service and endpoint behaviors are accidentally configured together;
  • abandoned client activity timeout protection.

SOAP Fault validation runs in a bounded isolated process because some .NET Framework test-host combinations do not terminate cleanly after a self-hosted fault channel. This does not change the production WCF lifecycle: the library does not install an IErrorHandler, inspect the SOAP payload, defer the customer's fault, or participate in fault construction.

HTTPS, WS-Security, custom bindings, and customer-specific certificate chains should still be validated in the customer's staging environment before fleet-wide rollout.

SQL And EF6

SqlClient instrumentation is enabled by default through ELVEN_ENABLE_SQL=true.

SQL statements and SQL parameters are redacted by default before export. The library keeps the operation shape while protecting values.

EF6 requires explicit registration:

dotnet add package Elven.Observability.NotaCarioca.EF6
using Elven.Observability.NotaCarioca;
using Elven.Observability.NotaCarioca.EF6;

ElvenObservability.Initialize();
ElvenEf6Instrumentation.Register();

Call ElvenEf6Instrumentation.Register() once during startup before EF6 queries start.

Dapper is usually covered through the underlying ADO.NET/SqlClient calls when SQL instrumentation is enabled.

Oracle ADO.NET

Oracle support is intentionally implemented as an explicit helper instead of a driver/profiler hook. This avoids forcing an Oracle.ManagedDataAccess upgrade in critical legacy apps.

Install:

Install-Package Elven.Observability.NotaCarioca.Oracle -Version 0.1.0-alpha.4 -Prerelease

Enable it:

<appSettings>
  <add key="ELVEN_ENABLE_ORACLE" value="true" />
</appSettings>

Wrap the central ADO.NET execution point, not every repository method. In most legacy systems there is one DBCommand, DataAccess, or OracleHelper class where commands are executed:

using Elven.Observability.NotaCarioca.Oracle;

var result = ElvenOracleInstrumentation.ExecuteScalar(
    command,
    () => command.ExecuteScalar(),
    new
    {
        business_operation = "GerarNfse",
        journey = "nfse",
        datastore = "oracle"
    });

For VB.NET:

Imports Elven.Observability.NotaCarioca.Oracle

Dim result = ElvenOracleInstrumentation.ExecuteScalar(
    command,
    Function() command.ExecuteScalar(),
    New With {
        .business_operation = "GerarNfse",
        .journey = "nfse",
        .datastore = "oracle"
    })

The helper records a client span and a duration histogram named db.client.operation.duration.

Common tags:

Attribute Example
db.system.name oracle
db.operation.name SELECT, UPDATE, BEGIN
db.namespace database/schema name when available
server.address Oracle data source when exposed by the provider
db.statement / db.query.text statement shape with literals redacted
db.query.parameter.count number of parameters
status ok or error
error.category classifier output

By default the SQL/PLSQL statement shape is kept but literals, CPF/CNPJ, e-mail, tokens, GUIDs, binary values, and parameter values are redacted before export. ELVEN_MAX_STATEMENT_LENGTH bounds the exported statement after redaction, and truncated statements end with [TRUNCATED]. Do not add raw SQL parameters to ELVEN_ALLOWED_RAW_ATTRIBUTES.

Logging Bridges

Logs are exported through OpenTelemetry logs and are enriched with:

  • trace_id
  • span_id
  • correlation_id
  • service name
  • environment
  • service version when configured
  • host name
  • process id

Each bridge has its own kill switch. Installing an appender/target/sink is not enough; the corresponding ELVEN_ENABLE_* flag must also be true.

Log bodies can be controlled separately from the bridge and from trace correlation:

Mode What is exported
redacted Default. Keeps the log message/XML readable, redacts sensitive values, preserves trace/span/correlation fields.
metadata Emits the log record with attributes and correlation, but replaces the body with [LOG_BODY_SUPPRESSED]. Useful when XML bodies are too large or too sensitive.
off Drops log records emitted by Elven legacy bridges. Use the bridge kill switches when you want per-logger-family control.
passthrough / raw Keeps non-sensitive body content as-is but still applies hard redaction for credentials, tokens, documents, cards, cookies, sessions, and SOAP security values. Use only for controlled debugging.

ELVEN_MAX_LOG_BODY_LENGTH bounds the body emitted by Elven bridges after redaction. Increase it only for controlled troubleshooting windows; very large XML log bodies can dominate exporter, Collector, and Loki costs.

Example:

<appSettings>
  <add key="ELVEN_ENABLE_LOG4NET" value="true" />
  <add key="ELVEN_LOG_BODY_MODE" value="redacted" />
</appSettings>

log4net

Install:

dotnet add package Elven.Observability.NotaCarioca.Logging.Log4Net

Config:

<appSettings>
  <add key="ELVEN_ENABLE_LOG4NET" value="true" />
</appSettings>

<log4net>
  <appender name="Elven"
            type="Elven.Observability.NotaCarioca.Logging.Log4Net.ElvenLog4NetAppender, Elven.Observability.NotaCarioca.Logging.Log4Net" />
  <root>
    <level value="INFO" />
    <appender-ref ref="Elven" />
  </root>
</log4net>

NLog

Install:

dotnet add package Elven.Observability.NotaCarioca.Logging.NLog

Config:

<appSettings>
  <add key="ELVEN_ENABLE_NLOG" value="true" />
</appSettings>

<nlog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <extensions>
    <add assembly="Elven.Observability.NotaCarioca.Logging.NLog" />
  </extensions>

  <targets>
    <target xsi:type="ElvenObservability"
            name="elven"
            layout="${longdate}|${level}|${logger}|${message}" />
  </targets>

  <rules>
    <logger name="*" minlevel="Info" writeTo="elven" />
  </rules>
</nlog>

Serilog

Install:

dotnet add package Elven.Observability.NotaCarioca.Logging.Serilog

Code:

using Elven.Observability.NotaCarioca.Logging.Serilog;
using Serilog;

Log.Logger = new LoggerConfiguration()
    .WriteTo.ElvenObservability()
    .CreateLogger();

Config:

<appSettings>
  <add key="ELVEN_ENABLE_SERILOG" value="true" />
</appSettings>

System.Diagnostics.Trace

Install:

dotnet add package Elven.Observability.NotaCarioca.Logging.SystemDiagnostics

Code:

using Elven.Observability.NotaCarioca.Logging.SystemDiagnostics;

ElvenSystemDiagnosticsBridge.Install();

Config:

<appSettings>
  <add key="ELVEN_ENABLE_SYSTEM_DIAGNOSTICS" value="true" />
</appSettings>

EventLog has a reserved config flag in this release, but no concrete EventLog bridge is shipped yet. Do not rely on EventLog ingestion until a dedicated bridge is added and tested.

Business Metrics

Business metrics are for product and journey dashboards, not just infrastructure dashboards.

Good examples:

  • nfses created
  • payment approvals
  • payment denials
  • external service latency
  • fraud analysis decisions
  • retries by provider
  • queue depth
  • active sessions

Bad examples:

  • metric names containing a user id
  • tags containing raw CPF, card, token, e-mail, phone, or session id
  • tags with thousands of possible values
  • creating a new metric name per provider, customer, or nfse

Mental Model

Use metric names for stable business facts.

Use tags for low-cardinality dimensions.

Good:

ElvenMetrics.Counter("nfse.document.created")
    .Add(1, new { provider = "nota-carioca", journey = "emissao", status = "success" });

Avoid:

ElvenMetrics.Counter("nfse.document.created." + nfseId).Add(1);

Naming Conventions

Use lowercase dot-separated names:

  • nfse.session.opened
  • nfse.consulta.requested
  • nfse.consulta.completed
  • nfse.document.created
  • payment.authorized
  • payment.denied
  • external_service.latency
  • external_service.retry.count
  • fraud.analysis.completed

Use stable tag names:

  • journey
  • business.operation
  • provider
  • channel
  • source
  • status
  • result
  • error.category
  • payment.gateway
  • payment.status
  • correlation_id

Identifiers such as client_id, nfse.id, session.id, user.id, CPF/CNPJ, e-mail, phone, card, token, password, cookie, and API keys are protected by default. Ordinary correlation_id values are preserved because they are needed for cross-signal correlation, but sensitive-looking values are still sanitized.

C# anonymous objects cannot use dotted property names. In simple examples, use CLR-friendly aliases such as payment_gateway or error_category. If a dashboard or collector rule requires the exact dotted attribute name, pass a dictionary or annotate a DTO property with ElvenBusinessAttribute:

ElvenMetrics.Counter("payment.authorized")
    .Add(1, new Dictionary<string, object>
    {
        ["payment.gateway"] = "rede",
        ["payment.status"] = "approved",
        ["journey"] = "pagamento"
    });

Counter

Use a counter for events that only go up.

ElvenMetrics.Counter("nfse.document.created", "NFS-e documents created")
    .Add(1, new
    {
        journey = "emissao",
        provider = "nota-carioca",
        status = "success"
    });

Use counters for:

  • created NFS-e documents
  • opened sessions
  • failed payments
  • provider errors
  • retries
  • validation failures
  • business rule denials

Duration

Use duration for latency or elapsed time. Values are exported in milliseconds.

var started = DateTimeOffset.UtcNow;

try
{
    // external service call
}
finally
{
    var elapsed = DateTimeOffset.UtcNow - started;

    ElvenMetrics.Duration("external_service.latency", "External service call latency")
        .Record(elapsed, new
        {
            provider = "prefeitura",
            operation = "consulta",
            status = "success"
        });
}

You can also record milliseconds directly:

ElvenMetrics.Duration("external_service.latency")
    .Record(123.4, new { provider = "nota-carioca", operation = "consulta_nfse" });

Use duration for:

  • external service call latency
  • payment gateway latency
  • fraud analysis latency
  • nfse workflow elapsed time
  • cache lookup latency
  • database dependency latency when business-specific context matters

UpDownCounter

Use an up/down counter for values that can increase and decrease.

ElvenMetrics.UpDownCounter("nfse.sessions.active", "Active nfse sessions")
    .Add(1, new { journey = "login" });

try
{
    // active session work
}
finally
{
    ElvenMetrics.UpDownCounter("nfse.sessions.active")
        .Add(-1, new { journey = "login" });
}

Use up/down counters for:

  • active sessions
  • in-flight external service calls
  • queue depth deltas
  • active payment attempts

Full Journey Example

This pattern creates one business span and several metrics for the same user journey.

using Elven.Observability.NotaCarioca;

public NfseResponse EmitirNfse(NfseRequest request)
{
    using (var span = ElvenTrace.StartBusinessSpan("EmitirNfse", new
    {
        journey = "emissao",
        provider = request.Provider,
        source = "nota-carioca",
        client_id = request.ClientId
    }))
    {
        var started = DateTimeOffset.UtcNow;

        try
        {
            ElvenMetrics.Counter("nfse.document.attempted")
                .Add(1, new
                {
                    journey = "emissao",
                    provider = request.Provider
                });

            var response = providerClient.EmitirNfse(request);

            ElvenMetrics.Counter("nfse.document.created")
                .Add(1, new
                {
                    journey = "emissao",
                    provider = request.Provider,
                    status = "success"
                });

            span.SetAttribute("result", "success");
            return response;
        }
        catch (TimeoutException ex)
        {
            span.RecordException(ex);
            span.SetAttribute("error.category", "provider_error");

            ElvenMetrics.Counter("nfse.document.failed")
                .Add(1, new
                {
                    journey = "emissao",
                    provider = request.Provider,
                    error_category = "provider_error"
                });

            throw;
        }
        finally
        {
            ElvenMetrics.Duration("nfse.document.duration")
                .Record(DateTimeOffset.UtcNow - started, new
                {
                    journey = "emissao",
                    provider = request.Provider
                });
        }
    }
}

For a large legacy system, centralize names in one class. This keeps dashboards stable.

public static class NfseTelemetry
{
    public static void NfseCreated(string provider, string journey)
    {
        ElvenMetrics.Counter("nfse.document.created")
            .Add(1, new
            {
                provider = provider,
                journey = journey,
                status = "success"
            });
    }

    public static void ProviderLatency(string provider, string operation, TimeSpan elapsed)
    {
        ElvenMetrics.Duration("external_service.latency")
            .Record(elapsed, new
            {
                provider = provider,
                operation = operation
            });
    }
}

Then call:

NfseTelemetry.NfseCreated("nota-carioca", "emissao");
NfseTelemetry.ProviderLatency("prefeitura", "consulta", elapsed);

Tag Safety

Metric tags are sanitized before export.

The library:

  • redacts sensitive keys such as password, token, api_key, authorization, cookie, cpf, cnpj, card, and document;
  • redacts sensitive-looking values such as e-mails, CPF/CNPJ, card numbers, bearer/basic tokens, and phones;
  • hashes known identifiers such as client_id, user.id, nfse.id, and session.id;
  • preserves ordinary correlation_id values for correlation;
  • limits tag count through ELVEN_MAX_METRIC_TAG_COUNT;
  • limits tag value length through ELVEN_MAX_TAG_VALUE_LENGTH.

You can rename DTO properties with ElvenBusinessAttribute and force redaction with ElvenSensitive:

public sealed class NfseMetricTags
{
    [ElvenBusinessAttribute("provider")]
    public string Provider { get; set; }

    [ElvenBusinessAttribute("journey")]
    public string Journey { get; set; }

    [ElvenSensitive]
    public string RawDocument { get; set; }
}

Usage:

ElvenMetrics.Counter("nfse.document.created")
    .Add(1, new NfseMetricTags
    {
        Provider = "nota-carioca",
        Journey = "emissao",
        RawDocument = request.Cpf
    });

RawDocument will be exported as [REDACTED].

Business Spans

Use business spans around important operations. They create trace spans with business attributes and connect manual telemetry to automatic WCF, HTTP, and SQL spans.

using (ElvenTrace.StartBusinessSpan("AbrirSessao", new
{
    client_id = clientId,
    journey = "login",
    provider = "nota-carioca"
}))
{
    // customer code
}

For exception handling:

using (var span = ElvenTrace.StartBusinessSpan("Pagamento", new { journey = "pagamento" }))
{
    try
    {
        gateway.Pay(request);
        span.SetAttribute("payment.status", "approved");
    }
    catch (Exception ex)
    {
        span.RecordException(ex);
        span.SetAttribute("payment.status", "failed");
        throw;
    }
}

For simple wrappers:

var result = ElvenTrace.Run("Antifraude", () =>
{
    return fraudClient.Analyze(request);
}, new
{
    journey = "pagamento",
    provider = "fraud-provider"
});

Async:

var result = await ElvenTrace.RunAsync("Gateway", async () =>
{
    return await gateway.AuthorizeAsync(request).ConfigureAwait(false);
}, new
{
    journey = "pagamento",
    payment_gateway = "rede"
}).ConfigureAwait(false);

You can add context later:

ElvenTrace.SetAttribute("provider", "nota-carioca");
ElvenTrace.SetBusinessContext(new { journey = "emissao", result = "success" });

Privacy Defaults

Default behavior:

  • SQL statement shape is preserved, but literals, comments, GUIDs, binary values, comparison values, and IN (...) lists are redacted. SQL parameters are fully redacted.
  • SOAP/XML shape is preserved, but sensitive element and attribute values are redacted.
  • Authorization, cookie, session, password, token, API key, SOAP security, card, CPF/CNPJ, e-mail, and phone values are redacted.
  • Known identifiers such as client_id, user.id, nfse.id, and session.id are hashed.
  • HTTP and SOAP payloads are not collected by default.
  • Exception stack traces are not exported by default.
  • Metric tag count and tag value length are bounded.

Raw attributes require an exact allowlist:

<appSettings>
  <add key="ELVEN_ALLOWED_RAW_ATTRIBUTES" value="db.query.summary" />
</appSettings>

Do not allowlist secrets, tokens, documents, card data, cookies, raw SQL parameters, or raw payload fields.

ELVEN_REDACTION_MODE=none disables soft redaction for controlled tests, but hard secrets are still blocked. If you need to keep XML logs usable while protecting credentials, prefer the default ELVEN_LOG_BODY_MODE=redacted instead of disabling redaction globally.

To include exception stack traces in a controlled environment:

<appSettings>
  <add key="ELVEN_CAPTURE_EXCEPTION_STACKTRACE" value="true" />
</appSettings>

Production Rollout Checklist

Before enabling in production:

  1. Confirm ELVEN_SERVICE_NAME, ELVEN_ENVIRONMENT, and Collector endpoint.
  2. Confirm Collector can receive traces, metrics, and logs on 4318.
  3. Enable one app first, not the whole fleet.
  4. Start with default redaction.
  5. Keep payload capture off.
  6. Keep WCF and EF6 opt-in.
  7. Add business metrics only with low-cardinality tags.
  8. Verify dashboards and traces in staging.
  9. Test rollback with ELVEN_ENABLED=false.
  10. Confirm app shutdown or recycle flush behavior where possible.

Troubleshooting

No telemetry:

  • Check ELVEN_ENABLED is not false.
  • Check OTEL_TRACES_EXPORTER, OTEL_METRICS_EXPORTER, and OTEL_LOGS_EXPORTER are not none.
  • Check OTEL_EXPORTER_OTLP_ENDPOINT points to the Collector, not directly to Elven.
  • Check firewall and DNS from the app server to the Collector.
  • Check Collector logs for rejected data.

No WCF spans:

  • Check Elven.Observability.NotaCarioca.Wcf is installed.
  • Check ELVEN_ENABLE_WCF=true.
  • Check the behavior extension is registered.
  • Check server services use behaviorConfiguration="observedService".
  • Check client endpoints use behaviorConfiguration="observedEndpoint".
  • Check the binding actually uses the service or client entry that has the behavior configuration.

No ASP.NET spans:

  • Check the HTTP module is in system.web/httpModules or system.webServer/modules.
  • Check the application pool is running .NET Framework 4.7.2 or 4.8.
  • Recycle the app pool after changing web.config.

No ASMX operation names:

  • Check ELVEN_ENABLE_ASMX=true.
  • Check the ASP.NET module is active for .asmx requests.
  • Check the client sends a useful SOAPAction header, or that the request path includes method PathInfo.

No EF6 spans:

  • Check Elven.Observability.NotaCarioca.EF6 is installed.
  • Check ELVEN_ENABLE_EF6=true.
  • Check ElvenEf6Instrumentation.Register() runs before EF6 commands execute.

No logs:

  • Check ELVEN_ENABLE_LOG4NET, ELVEN_ENABLE_NLOG, ELVEN_ENABLE_SERILOG, or ELVEN_ENABLE_SYSTEM_DIAGNOSTICS.
  • Check the appender, target, sink, or listener is actually registered in the app logging framework.
  • Check OTEL_LOGS_EXPORTER is not none.

Assembly binding issues:

  • Legacy apps may need binding redirects after installing NuGet packages.
  • For executable projects, enable automatic binding redirects where possible.
  • For web apps, review generated binding redirects in web.config.

Startup diagnostics:

foreach (var message in ElvenStartupDiagnostics.Snapshot())
{
    System.Diagnostics.Trace.WriteLine(message);
}

Build, Test, Pack

Run on Windows for full .NET Framework 4.7.2/4.8 coverage:

dotnet restore Elven.Observability.NotaCarioca.sln
dotnet build Elven.Observability.NotaCarioca.sln -c Release --no-restore
dotnet test tests/Elven.Observability.NotaCarioca.UnitTests/Elven.Observability.NotaCarioca.UnitTests.csproj -c Release --no-build
dotnet test tests/Elven.Observability.NotaCarioca.WcfIntegrationTests/Elven.Observability.NotaCarioca.WcfIntegrationTests.csproj -c Release --no-build
./samples/GaSmoke/bin/Release/net48/GaSmoke.exe
dotnet pack Elven.Observability.NotaCarioca.sln -c Release --no-build -o artifacts/packages

The package build produces both net472 and net48 assets. The GA smoke currently runs as a net48 executable because it exercises WCF and SQL Server LocalDB on Windows. CI also runs scripts/ci/run-aspnet-classic-smoke.ps1 to load the ASP.NET classic HTTP module under IIS Express.

Signing And Publishing

NuGet publication has two supported paths:

  • nuget-release: publishes unsigned author packages after all gates pass. NuGet.org applies repository signing after acceptance.
  • signed-release: signs packages with an Elven code-signing certificate before publishing. Use this path when Elven author signing is available.

Manual signing and publish:

./scripts/nuget/sign-packages.ps1
./scripts/nuget/publish-signed-packages.ps1 -PublishSymbols

Manual unsigned publish:

dotnet pack Elven.Observability.NotaCarioca.sln -c Release --no-build -o artifacts/packages
./scripts/nuget/publish-unsigned-packages.ps1 -PublishSymbols

Do not publish from regular CI artifacts. Use either the manual nuget-release workflow for unsigned publication or the manual signed-release workflow when author signing is configured.

See docs/signing-and-publishing.md for both release paths, NuGet.org certificate registration, and GitHub secret setup.

Samples

Known Limitations

  • EventLog ingestion is reserved but not implemented as a concrete bridge in this release.
  • WCF HTTPS and message security should be validated with customer-like certificates and bindings before broad rollout.
  • CaptureSoapPayload, CaptureSoapHeaders, AllowedRequestHeaders, and AllowedResponseHeaders are reserved compatibility options and are intentionally no-op in this release. Setting them emits a bounded startup diagnostic; it never captures payloads or application headers.
  • Local dotnet test for .NET Framework may not run on non-Windows or arm64 machines. Windows CI is the authoritative test host.
Product Compatible and additional computed target framework versions.
.NET Framework net472 is compatible.  net48 is compatible.  net481 was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.0-alpha.4 41 7/15/2026
0.1.0-alpha.3 53 7/6/2026
0.1.0-alpha.2 53 7/6/2026