Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1868,7 +1868,7 @@ PS > seqcli signal list -i signal-m33302 --json
{"Title": "Alarms", "Description": "Automatically created", "Filters": [{"De...
```

## Store-and-forward ingestion proxy (preview)
## Store-and-forward ingestion proxy

The `seqcli forwarder` family of commands provide simple, durable ingestion buffering for occasionally-connected and
intermittently-disconnected systems. The forwarder implements the Seq ingestion API, so applications that write
Expand All @@ -1890,12 +1890,9 @@ destination Seq server.
To start a forwarder instance at the terminal, listening on port 5341 and forwarding to `seq.example.com`, run:

```shell
seqcli forwarder run --pre --listen http://127.0.0.1:5341 -s https://seq.example.com
seqcli forwarder run --listen http://127.0.0.1:5341 -s https://seq.example.com
```

> While the `forwarder` command group is in preview, all `forwarder` commands require the `--pre` switch; you'll
> also need to supply `--pre` when requesting help, e.g. `seqcli help forwarder run --pre`.

You can test your forwarder using the `seqcli log` command:

```shell
Expand Down
27 changes: 25 additions & 2 deletions src/SeqCli/Cli/Commands/Mcp/RunCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,21 @@

using System;
using System.Threading.Tasks;
using Autofac;
using Autofac.Builder;
using Autofac.Core;
using Autofac.Core.Resolving.Pipeline;
using Autofac.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using SeqCli.Api;
using SeqCli.Cli.Features;
using SeqCli.Config;
using SeqCli.Mcp;
using SeqCli.Mcp.Tools.Metrics;
using SeqCli.Mcp.Tools.Query;
using SeqCli.Mcp.Tools.Search;
using SeqCli.Mcp.Tools.Signals;
using Serilog;

namespace SeqCli.Cli.Commands.Mcp;
Expand Down Expand Up @@ -58,15 +65,31 @@ protected override async Task<int> Run()
try
{
var builder = Host.CreateApplicationBuilder();
builder.ConfigureContainer(new AutofacServiceProviderFactory());
builder.ConfigureContainer(new AutofacServiceProviderFactory(container =>
{
// The MCP SDK tries to use the container to resolve any parameter type; Autofac's default collection
// registrations cause array parameters to resolve to empty arrays. We thwart this by short-circuiting
// the search for matching registrations.
var stringArray = new TypedService(typeof(string[]));
container.RegisterServiceMiddleware<string[]>(PipelinePhase.ResolveRequestStart, (rr, ctx) =>
{
if (rr.Service == stringArray)
return;

ctx(rr);
});
}));
builder.Services.AddSerilog();
builder.Services.AddSingleton(_ => SeqConnectionFactory.Connect(_connection, config));
builder.Services.AddSingleton<McpSession>();
builder.Services
.AddMcpServer()
.WithStdioServerTransport()
.WithTools([
typeof(SearchAndQueryToolType)
typeof(SearchTools),
typeof(MetricsTools),
typeof(QueryTools),
typeof(SignalTools)
]);

await builder.Build().RunAsync();
Expand Down
102 changes: 102 additions & 0 deletions src/SeqCli/Cli/Commands/Metrics/DimensionCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Copyright © Datalust Pty Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System;
using System.Globalization;
using System.Threading.Tasks;
using SeqCli.Api;
using SeqCli.Cli.Features;
using SeqCli.Config;
using Serilog;

namespace SeqCli.Cli.Commands.Metrics;

[Command("metrics", "dimension", "List distinct values for a metric dimension",
Comment thread
nblumhardt marked this conversation as resolved.
Outdated
Example = "seqcli metrics dimension --accessor @Resource.service.name")]
class DimensionCommand : Command
{
readonly ConnectionFeature _connection;
readonly OutputFormatFeature _output;
readonly DateRangeFeature _range;
readonly StoragePathFeature _storagePath;
string? _accessor;
int _count = 30;
bool _trace;

public DimensionCommand()
{
Options.Add(
"d=|accessor=",
"The dimension accessor, e.g. `cpu.mode`",
v => _accessor= v);

Options.Add(
"c=|count=",
$"The maximum number of dimensions to retrieve; the default is {_count}",
v => _count = int.Parse(v, CultureInfo.InvariantCulture));

_range = Enable<DateRangeFeature>();
_output = Enable(new OutputFormatFeature(supportNative: true));
_storagePath = Enable<StoragePathFeature>();

Options.Add("trace", "Enable detailed (server-side) query tracing", _ => _trace = true);

_connection = Enable<ConnectionFeature>();
}

protected override async Task<int> Run()
{
try
{
if (string.IsNullOrWhiteSpace(_accessor))
{
Log.Error("A dimension `--accessor` must be specified");
return 1;
}

var config = RuntimeConfigurationLoader.Load(_storagePath);
var output = _output.GetOutputFormat(config);
var connection = SeqConnectionFactory.Connect(_connection, config);

var result = await connection.Metrics.ListDimensionValuesAsync(
_accessor,
_count,
rangeStartUtc: _range.Start,
rangeEndUtc: _range.End,
trace: _trace);

if (output.Json)
{
// In the JSON case we write an array with all values.
output.WriteObject(result);
}
else
{
// Native and plain text formatting use one-per-line output (both allow multi-line strings, but
// string boundaries are clearer in native mode).
foreach (var value in result)
{
output.WriteObject(value);
}
}

return 0;
}
catch (Exception ex)
{
Log.Error(ex, "Could not retrieve metrics: {ErrorMessage}", ex.Message);
return 1;
}
}
}
93 changes: 93 additions & 0 deletions src/SeqCli/Cli/Commands/Metrics/DimensionsCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Copyright © Datalust Pty Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System;
using System.Globalization;
using System.Threading.Tasks;
using SeqCli.Api;
using SeqCli.Cli.Features;
using SeqCli.Config;
using Serilog;

namespace SeqCli.Cli.Commands.Metrics;

[Command("metrics", "dimensions", "List the dimensions associated with a given metric",
Example = "seqcli metrics dimensions -m http.response.status_code")]
class DimensionsCommand : Command
{
readonly ConnectionFeature _connection;
readonly OutputFormatFeature _output;
readonly DateRangeFeature _range;
readonly StoragePathFeature _storagePath;
string? _metric;
int _count = 30;
bool _trace;

public DimensionsCommand()
{
Options.Add(
"m=|metric=",
"A metric name, for example `hats-sold` or `http.request.duration`; omit to list dimensions for all metrics",
v => _metric= v);

Options.Add(
"c=|count=",
$"The maximum number of dimensions to retrieve; the default is {_count}",
v => _count = int.Parse(v, CultureInfo.InvariantCulture));

_range = Enable<DateRangeFeature>();
_output = Enable<OutputFormatFeature>();
_storagePath = Enable<StoragePathFeature>();

Options.Add("trace", "Enable detailed (server-side) query tracing", _ => _trace = true);

_connection = Enable<ConnectionFeature>();
}

protected override async Task<int> Run()
{
try
{
var config = RuntimeConfigurationLoader.Load(_storagePath);
var output = _output.GetOutputFormat(config);
var connection = SeqConnectionFactory.Connect(_connection, config);

var result = await connection.Metrics.ListDimensionsAsync(
_count,
_metric,
rangeStartUtc: _range.Start,
rangeEndUtc: _range.End,
trace: _trace);

if (output.Json)
{
output.WriteObject(result);
}
else
{
foreach (var dimension in result)
{
output.WriteText(dimension.Accessor);
}
}

return 0;
}
catch (Exception ex)
{
Log.Error(ex, "Could not retrieve metrics: {ErrorMessage}", ex.Message);
return 1;
}
}
}
Loading
Loading