Skip to content
Open
Show file tree
Hide file tree
Changes from all 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",
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