{"id":17182350,"url":"https://github.com/simoncropp/seqproxy","last_synced_at":"2025-09-14T18:23:55.306Z","repository":{"id":34906826,"uuid":"188326323","full_name":"SimonCropp/SeqProxy","owner":"SimonCropp","description":"Enables writing seq logs by proxying requests through an ASP.NET Controller or Middleware.","archived":false,"fork":false,"pushed_at":"2025-09-14T02:47:01.000Z","size":1136,"stargazers_count":20,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-09-14T04:22:36.624Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SimonCropp.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"license.txt","code_of_conduct":"code_of_conduct.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"SimonCropp"}},"created_at":"2019-05-24T00:40:49.000Z","updated_at":"2025-09-14T02:47:05.000Z","dependencies_parsed_at":"2023-02-12T22:16:19.847Z","dependency_job_id":"fee18159-27e2-4f05-8a21-9c751c5821d3","html_url":"https://github.com/SimonCropp/SeqProxy","commit_stats":{"total_commits":1026,"total_committers":4,"mean_commits":256.5,"dds":0.5019493177387915,"last_synced_commit":"7f0926458f4689d4eb9eb39d50caaa7984300aac"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/SimonCropp/SeqProxy","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SimonCropp%2FSeqProxy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SimonCropp%2FSeqProxy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SimonCropp%2FSeqProxy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SimonCropp%2FSeqProxy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SimonCropp","download_url":"https://codeload.github.com/SimonCropp/SeqProxy/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SimonCropp%2FSeqProxy/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":275145754,"owners_count":25413282,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-14T02:00:10.474Z","response_time":75,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-15T00:36:51.283Z","updated_at":"2025-09-14T18:23:55.235Z","avatar_url":"https://github.com/SimonCropp.png","language":"C#","funding_links":["https://github.com/sponsors/SimonCropp"],"categories":[],"sub_categories":[],"readme":"# \u003cimg src=\"/src/icon.png\" height=\"30px\"\u003e SeqProxy\n\n[![Build status](https://ci.appveyor.com/api/projects/status/7996jd4uoooy5qy2/branch/main?svg=true)](https://ci.appveyor.com/project/SimonCropp/SeqProxy)\n[![NuGet Status](https://img.shields.io/nuget/v/SeqProxy.svg)](https://www.nuget.org/packages/SeqProxy/)\n\nEnables writing [Seq](https://datalust.co/seq) logs by proxying requests through an ASP.NET Controller or Middleware.\n\n**See [Milestones](../../milestones?state=closed) for release notes.**\n\n\n## Why\n\n * Avoid exposing the Seq API to the internet.\n * Leverage [Asp Authentication and Authorization](https://docs.microsoft.com/en-us/aspnet/core/security/) to verify and control incoming requests.\n * Append [extra data](#extra-data) to log messages during server processing.\n\n\n## NuGet package\n\nhttps://nuget.org/packages/SeqProxy/\n\n\n## HTTP Format/Protocol\n\nFormat: [Serilog compact](https://github.com/serilog/serilog-formatting-compact).\n\nProtocol: [Seq raw events](https://docs.datalust.co/docs/posting-raw-events).\n\nNote that timestamp (`@t`) is optional when using this project. If it is not supplied the server timestamp will be used.\n\n\n## Extra data\n\nFor every log entry written the following information is appended:\n\n * The current application name (as `Application`) defined in code at startup.\n * The current application version (as `ApplicationVersion`) defined in code at startup.\n * The server name (as `Server`) using `Environment.MachineName`.\n * All claims for the current User from `ControllerBase.User.Claims`.\n * The [user-agent header](https://en.wikipedia.org/wiki/User_agent) as `UserAgent`.\n * The [referer header](https://en.wikipedia.org/wiki/HTTP_referer) as `Referrer`.\n\n\u003cimg src=\"/src/extraData.png\"\u003e\n\n\n### SeqProxyId\n\nSeqProxyId is a tick based timestamp to help correlating a front-end error with a Seq log entry.\n\nIt is appended to every Seq log entry and returned as a header to HTTP response.\n\nThe id is generated using the following:\n\n\u003c!-- snippet: BuildId --\u003e\n\u003ca id='snippet-BuildId'\u003e\u003c/a\u003e\n```cs\nvar startOfYear = new DateTime(utcNow.Year, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);\nvar ticks = utcNow.Ticks - startOfYear.Ticks;\nvar id = ticks.ToString(\"x\");\n```\n\u003csup\u003e\u003ca href='/src/SeqProxy/SeqWriter.cs#L94-L100' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-BuildId' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\nWhich generates a string of the form `8e434f861302`. The current year is trimmed to shorten the id and under the assumption that retention policy is not longer than 12 months. There is a small chance of collisions, but given the use-case (error correlation), this should not impact the ability to find the correct error. This string can then be given to a user as a error correlation id.\n\nThen the log entry can be accessed using a Seq filter.\n\n`http://seqServer/#/events?filter=SeqProxyId%3D'39f616eeb2e3'`\n\n\n## Usage\n\n\n### Enable in Startup\n\nEnable in `Startup.ConfigureServices`\n\n\u003c!-- snippet: ConfigureServices --\u003e\n\u003ca id='snippet-ConfigureServices'\u003e\u003c/a\u003e\n```cs\npublic void ConfigureServices(IServiceCollection services)\n{\n    services.AddMvcCore(option =\u003e option.EnableEndpointRouting = false);\n    services.AddSeqWriter(seqUrl: \"http://localhost:5341\");\n}\n```\n\u003csup\u003e\u003ca href='/src/SampleWeb/Startup.cs#L5-L13' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-ConfigureServices' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\nThere are several optional parameters:\n\n\u003c!-- snippet: ConfigureServicesFull --\u003e\n\u003ca id='snippet-ConfigureServicesFull'\u003e\u003c/a\u003e\n```cs\npublic void ConfigureServices(IServiceCollection services)\n{\n    services.AddMvcCore();\n    services.AddSeqWriter(\n        seqUrl: \"http://localhost:5341\",\n        apiKey: \"TheApiKey\",\n        application: \"MyAppName\",\n        appVersion: new(1, 2),\n        scrubClaimType: claimType =\u003e\n        {\n            var lastIndexOf = claimType.LastIndexOf('/');\n            if (lastIndexOf == -1)\n            {\n                return claimType;\n            }\n\n            return claimType[(lastIndexOf + 1)..];\n        });\n}\n```\n\u003csup\u003e\u003ca href='/src/Tests/FullStartupConfig.cs#L4-L26' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-ConfigureServicesFull' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\n * `application` defaults to `Assembly.GetCallingAssembly().GetName().Name`.\n * `applicationVersion` defaults to `Assembly.GetCallingAssembly().GetName().Version`.\n * `scrubClaimType` is used to clean up claimtype strings. For example [ClaimTypes.Email](https://docs.microsoft.com/en-us/dotnet/api/system.identitymodel.claims.claimtypes.email?System_IdentityModel_Claims_ClaimTypes_Email) is `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress`, but when recording to Seq the value `emailaddress` is sufficient. Defaults to `DefaultClaimTypeScrubber.Scrub` to get the string after the last `/`.\n\n\u003c!-- snippet: DefaultClaimTypeScrubber.cs --\u003e\n\u003ca id='snippet-DefaultClaimTypeScrubber.cs'\u003e\u003c/a\u003e\n```cs\nnamespace SeqProxy;\n\n/// \u003csummary\u003e\n/// Used for scrubbing claims when no other scrubber is defined.\n/// \u003c/summary\u003e\npublic static class DefaultClaimTypeScrubber\n{\n    /// \u003csummary\u003e\n    /// Get the string after the last /.\n    /// \u003c/summary\u003e\n    public static CharSpan Scrub(CharSpan claimType)\n    {\n        Guard.AgainstEmpty(claimType, nameof(claimType));\n        var lastIndexOf = claimType.LastIndexOf('/');\n        if (lastIndexOf == -1)\n        {\n            return claimType;\n        }\n\n        return claimType[(lastIndexOf + 1)..];\n    }\n}\n```\n\u003csup\u003e\u003ca href='/src/SeqProxy/DefaultClaimTypeScrubber.cs#L1-L22' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-DefaultClaimTypeScrubber.cs' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\n\n### Add HTTP handling\n\nThere are two approaches to handling the HTTP containing log events. Using a Middleware and using a Controller.\n\n\n#### Using a Middleware\n\nUsing a [Middleware](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/) is done by calling `SeqWriterConfig.UseSeq` in `Startup.Configure(IApplicationBuilder builder)`:\n\n\u003c!-- snippet: ConfigureBuilder --\u003e\n\u003ca id='snippet-ConfigureBuilder'\u003e\u003c/a\u003e\n```cs\npublic void Configure(IApplicationBuilder builder)\n{\n    builder.UseSeq();\n```\n\u003csup\u003e\u003ca href='/src/SampleWeb/Startup.cs#L15-L20' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-ConfigureBuilder' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\n\n##### Authorization\n\nAuthorization in the middleware can bu done by using `useAuthorizationService = true` in `UseSeq`.\n\n\u003c!-- snippet: StartupWithAuth --\u003e\n\u003ca id='snippet-StartupWithAuth'\u003e\u003c/a\u003e\n```cs\npublic void Configure(IApplicationBuilder builder)\n{\n    builder.UseSeq(useAuthorizationService: true);\n```\n\u003csup\u003e\u003ca href='/src/Tests/StartupWithAuth.cs#L5-L10' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-StartupWithAuth' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\nThis then uses [IAuthorizationService](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/resourcebased) to verify the request:\n\n\u003c!-- snippet: HandleWithAuth --\u003e\n\u003ca id='snippet-HandleWithAuth'\u003e\u003c/a\u003e\n```cs\nasync Task HandleWithAuth(HttpContext context)\n{\n    var user = context.User;\n    var authResult = await authService.AuthorizeAsync(user, null, \"SeqLog\");\n\n    if (!authResult.Succeeded)\n    {\n        await context.ChallengeAsync();\n        return;\n    }\n\n    await writer.Handle(\n        user,\n        context.Request,\n        context.Response,\n        context.RequestAborted);\n}\n```\n\u003csup\u003e\u003ca href='/src/SeqProxy/SeqMiddlewareWithAuth.cs#L15-L35' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-HandleWithAuth' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\n\n#### Using a Controller\n\n`BaseSeqController` is an implementation of `ControllerBase` that provides a HTTP post and some basic routing.\n\n\u003c!-- snippet: BaseSeqController.cs --\u003e\n\u003ca id='snippet-BaseSeqController.cs'\u003e\u003c/a\u003e\n```cs\nnamespace SeqProxy;\n\n/// \u003csummary\u003e\n/// An implementation of \u003csee cref=\"ControllerBase\"/\u003e that provides a http post and some basic routing.\n/// \u003c/summary\u003e\n[Route(\"/api/events/raw\")]\n[Route(\"/seq\")]\n[ApiController]\npublic abstract class BaseSeqController :\n    ControllerBase\n{\n    SeqWriter writer;\n\n    /// \u003csummary\u003e\n    /// Initializes a new instance of \u003csee cref=\"BaseSeqController\"/\u003e\n    /// \u003c/summary\u003e\n    protected BaseSeqController(SeqWriter writer) =\u003e\n        this.writer = writer;\n\n    /// \u003csummary\u003e\n    /// Handles log events via a HTTP post.\n    /// \u003c/summary\u003e\n    [HttpPost]\n    public virtual Task Post() =\u003e\n        writer.Handle(User, Request, Response, HttpContext.RequestAborted);\n}\n```\n\u003csup\u003e\u003ca href='/src/SeqProxy/BaseSeqController.cs#L1-L26' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-BaseSeqController.cs' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\nAdd a new [controller](https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/actions) that overrides `BaseSeqController`.\n\n\u003c!-- snippet: SimpleController --\u003e\n\u003ca id='snippet-SimpleController'\u003e\u003c/a\u003e\n```cs\npublic class SeqController(SeqWriter writer) :\n    BaseSeqController(writer);\n```\n\u003csup\u003e\u003ca href='/src/Tests/ControllerSamples.cs#L3-L6' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-SimpleController' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\n\n##### Authorization/Authentication\n\nAdding authorization and authentication can be done with an [AuthorizeAttribute](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/simple).\n\n\u003c!-- snippet: AuthorizeController --\u003e\n\u003ca id='snippet-AuthorizeController'\u003e\u003c/a\u003e\n```cs\n[Authorize]\npublic class SeqController(SeqWriter writer) :\n    BaseSeqController(writer)\n```\n\u003csup\u003e\u003ca href='/src/Tests/ControllerSamples.cs#L28-L33' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-AuthorizeController' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\n\n##### Method level attributes\n\nMethod level Asp attributes can by applied by overriding `BaseSeqController.Post`.\n\nFor example adding an [exception filter ](https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters#exception-filters).\n\n\u003c!-- snippet: OverridePostController --\u003e\n\u003ca id='snippet-OverridePostController'\u003e\u003c/a\u003e\n```cs\npublic class SeqController(SeqWriter writer) :\n    BaseSeqController(writer)\n{\n    [CustomExceptionFilter]\n    public override Task Post() =\u003e\n        base.Post();\n```\n\u003csup\u003e\u003ca href='/src/Tests/ControllerSamples.cs#L11-L19' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-OverridePostController' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\n\n## Client Side Usage\n\n\n### Using raw JavaScript\n\nWriting to Seq can be done using a HTTP post:\n\n\u003c!-- snippet: LogRawJs --\u003e\n\u003ca id='snippet-LogRawJs'\u003e\u003c/a\u003e\n```js\nfunction LogRawJs(text) {\n    const postSettings = {\n        method: 'POST',\n        credentials: 'include',\n        body: `{'@mt':'RawJs input: {Text}','Text':'${text}'}`\n    };\n\n    return fetch('/api/events/raw', postSettings);\n}\n```\n\u003csup\u003e\u003ca href='/src/SampleWeb/sample.js#L59-L69' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-LogRawJs' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\n\n### Using Structured-Log\n\n[structured-log](https://github.com/structured-log/structured-log/) is a structured logging framework for JavaScript, inspired by Serilog.\n\nIn combination with [structured-log-seq-sink](https://github.com/Wedvich/structured-log-seq-sink) it can be used to write to Seq\n\nTo use this approach:\n\n\n#### Include the libraries\n\nInstall both [structured-log npm](https://www.npmjs.com/package/structured-log) and [structured-log-seq-sink npm](https://www.npmjs.com/package/structured-log-seq-sink). Or include them from [jsDelivr](https://www.jsdelivr.com/):\n\n\u003c!-- snippet: StructuredLogInclude --\u003e\n\u003ca id='snippet-StructuredLogInclude'\u003e\u003c/a\u003e\n```html\n\u003cscript src='https://cdn.jsdelivr.net/npm/structured-log/dist/structured-log.js'\u003e\n\u003c/script\u003e\n\u003cscript src='https://cdn.jsdelivr.net/npm/structured-log-seq-sink/dist/structured-log-seq-sink.js'\u003e\n\u003c/script\u003e\n```\n\u003csup\u003e\u003ca href='/src/SampleWeb/sample.html#L4-L9' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-StructuredLogInclude' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\n\n#### Configure the log\n\n\u003c!-- snippet: StructuredLogConfig --\u003e\n\u003ca id='snippet-StructuredLogConfig'\u003e\u003c/a\u003e\n```js\nvar levelSwitch = new structuredLog.DynamicLevelSwitch('info');\nconst log = structuredLog.configure()\n    .writeTo(new structuredLog.ConsoleSink())\n    .minLevel(levelSwitch)\n    .writeTo(SeqSink({\n        url: `${location.protocol}//${location.host}`,\n        compact: true,\n        levelSwitch: levelSwitch\n    }))\n    .create();\n```\n\u003csup\u003e\u003ca href='/src/SampleWeb/sample.js#L1-L12' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-StructuredLogConfig' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\n\n#### Write a log message\n\n\u003c!-- snippet: StructuredLog --\u003e\n\u003ca id='snippet-StructuredLog'\u003e\u003c/a\u003e\n```js\nfunction LogStructured(text) {\n    log.info('StructuredLog input: {Text}', text);\n}\n```\n\u003csup\u003e\u003ca href='/src/SampleWeb/sample.js#L50-L54' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-StructuredLog' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\n\n#### Including data but omitting from the message template\n\nWhen using structured-log, data not included in the message template will be named with a convention of `a+counter`. So for example if the following is logged:\n\n```\nlog.info('The text: {Text}', text, \"OtherData\");\n```\n\nThen `OtherData` would be written to Seq with the property name `a1`.\n\nTo work around this:\n\nInclude a filter that replaces a known token name (in this case `{@Properties}`):\n\n\u003c!-- snippet: StructuredLogConfigExtraProp --\u003e\n\u003ca id='snippet-StructuredLogConfigExtraProp'\u003e\u003c/a\u003e\n```js\nconst logWithExtraProps = structuredLog.configure()\n    .filter(logEvent =\u003e {\n        const template = logEvent.messageTemplate;\n        template.raw = template.raw.replace('{@Properties}','');\n        return true;\n    })\n    .writeTo(SeqSink({\n        url: `${location.protocol}//${location.host}`,\n        compact: true,\n        levelSwitch: levelSwitch\n    }))\n    .create();\n```\n\u003csup\u003e\u003ca href='/src/SampleWeb/sample.js#L14-L27' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-StructuredLogConfigExtraProp' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\nInclude that token name in the message template, and then include an object at the same position in the log parameters:\n\n\u003c!-- snippet: StructuredLogWithExtraProps --\u003e\n\u003ca id='snippet-StructuredLogWithExtraProps'\u003e\u003c/a\u003e\n```js\nfunction LogStructuredWithExtraProps(text) {\n    logWithExtraProps.info(\n        'StructuredLog input: {Text} {@Properties}',\n        text,\n        {\n            Timezone: new Date().getTimezoneOffset(),\n            Language: navigator.language\n        });\n}\n```\n\u003csup\u003e\u003ca href='/src/SampleWeb/sample.js#L38-L48' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-StructuredLogWithExtraProps' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\nThen a destructured property will be written to Seq.\n\n\u003cimg src=\"/src/omitFromMessage.png\"\u003e\n\n\n## Icon\n\n[Robot](https://thenounproject.com/term/robot/883226/) designed by [Maxim Kulikov](https://thenounproject.com/maxim221) from [The Noun Project](https://thenounproject.com).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimoncropp%2Fseqproxy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsimoncropp%2Fseqproxy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimoncropp%2Fseqproxy/lists"}