Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 1 addition & 1 deletion ci.global.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"sdk": {
"version": "10.0.201"
"version": "10.0.203"
}
}
2 changes: 1 addition & 1 deletion src/Roastery/Roastery.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Serilog" Version="4.3.0" />
<PackageReference Include="Serilog" Version="4.3.1" />
<PackageReference Include="SerilogTracing" Version="2.3.1" />
</ItemGroup>

Expand Down
2 changes: 1 addition & 1 deletion src/SeqCli/Cli/Commands/Dashboard/RenderCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ static string BuildSqlQuery(ChartQueryPart query, DateTime rangeStart, DateTime
foreach (var measurement in query.Measurements)
sql.Select(measurement.Value, measurement.Label);

sql.FromStream = true;
sql.From = query.DataSource.ToString().ToLowerInvariant();

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because DataSource is a value type (enum), this will fall back to Stream by default for older API versions that don't supply a data source explicitly (these only support stream anyway).


sql.Where($"@Timestamp >= DateTime('{rangeStart:O}')");
sql.Where($"@Timestamp < DateTime('{rangeEnd:O}')");
Expand Down
1 change: 0 additions & 1 deletion src/SeqCli/Cli/Commands/Forwarder/InstallCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.AccessControl;
using System.Threading.Tasks;
using SeqCli.Cli.Features;
Expand Down
62 changes: 49 additions & 13 deletions src/SeqCli/Cli/Commands/RetentionPolicy/CreateCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

using System;
using System.Threading.Tasks;
using Seq.Api.Model.Shared;
using Seq.Api.Model.Signals;
using SeqCli.Api;
using SeqCli.Cli.Features;
Expand All @@ -26,15 +27,16 @@
namespace SeqCli.Cli.Commands.RetentionPolicy;

[Command("retention", "create", "Create a retention policy",
Example = "seqcli retention create --after 30d --delete-all-events")]
Example = "seqcli retention create --after 30d --data-source stream --delete-all")]
class CreateCommand : Command
{
readonly ConnectionFeature _connection;
readonly OutputFormatFeature _output;
readonly StoragePathFeature _storagePath;

string? _afterDuration;
bool _deleteAllEvents;
bool _deleteAll;
string? _dataSource;
string? _deleteMatchingSignal;

public CreateCommand()
Expand All @@ -43,21 +45,36 @@ public CreateCommand()
"after=",
"A duration after which the policy will delete events, e.g. `7d`",
v => _afterDuration = ArgumentString.Normalize(v));

Options.Add(
"delete-all-events",
"The policy should delete all events (currently the only supported option)",
_ => _deleteAllEvents = true);
"data-source=",
"The data source to delete records from (`stream` for log events and traces, or `series` for metrics); defaults to `stream`",
v => _dataSource = ArgumentString.Normalize(v));

Options.Add(
"delete-all",
"The policy should delete all records in the target data source after the specified duration",
_ => _deleteAll = true);

Options.Add(
"delete=",
"Stream incoming events to this app instance as they're ingested; optionally accepts a signal expression limiting which events should be streamed",
"A signal expression identifying events that should be deleted; not supported by the `series` data source",
s =>
{
_deleteMatchingSignal = s;
}
);


// Maintained for compatibility in Seq 2026.x; intended to be retired in 2027.
Options.Add(
"delete-all-events",
"The policy should delete all events from `stream`",
_ =>
{
_deleteAll = true;
_dataSource = null;
}, hidden: true);

_connection = Enable<ConnectionFeature>();
_output = Enable<OutputFormatFeature>();
_storagePath = Enable<StoragePathFeature>();
Expand All @@ -71,19 +88,19 @@ protected override async Task<int> Run()
SignalExpressionPart? removedSignalExpression;

// Exactly one of `delete-all-events` or `delete` must be specified
if (_deleteAllEvents)
if (_deleteAll)
{
if (!string.IsNullOrEmpty(_deleteMatchingSignal))
{
Log.Error("Only one of the `delete-all-events` or `delete` options may be specified");
Log.Error("Only one of the `--delete-all` or `--delete` options may be specified");
return 1;
}

removedSignalExpression = null;
}
else if (string.IsNullOrEmpty(_deleteMatchingSignal))
{
Log.Error("Either the `delete-all-events` or `delete` options must be specified");
Log.Error("One of either the `--delete-all` or `--delete` options must be specified");
return 1;
}
else
Expand All @@ -93,15 +110,34 @@ protected override async Task<int> Run()

if (_afterDuration == null)
{
Log.Error("A duration must be specified using `after`");
Log.Error("A duration must be specified using `--after`");
return 1;
}

var duration = DurationMoniker.ToTimeSpan(_afterDuration);


if (_dataSource == null)
{
Log.Error("Use `--data-source` to specify `stream` or `series` as the retention target");
return 1;
}

if (!Enum.TryParse(_dataSource, out DataSource dataSource))
{
Log.Error("The `--data-source` option supports `stream` and `series`");
return 1;
}

if (removedSignalExpression != null && dataSource != DataSource.Stream)
{
Log.Error("The `--delete` option is only valid when `--data-source` is `stream`");
return 1;
}

var policy = await connection.RetentionPolicies.TemplateAsync();
policy.RetentionTime = duration;
policy.RemovedSignalExpression = removedSignalExpression;
policy.DataSource = dataSource;

policy = await connection.RetentionPolicies.AddAsync(policy);

Expand Down
112 changes: 112 additions & 0 deletions src/SeqCli/Cli/Commands/View/CreateCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Copyright 2018 Datalust Pty Ltd and Contributors
//
// 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.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Seq.Api.Model.Shared;
using Seq.Api.Model.Signals;
using SeqCli.Api;
using SeqCli.Cli.Features;
using SeqCli.Config;
using SeqCli.Util;
using Serilog;

namespace SeqCli.Cli.Commands.View;

[Command("view", "create", "Create a metrics view",
Example = "seqcli view create -t 'API' -g '@Scope.name' -f \"@Resource.service.name = 'seq_cafe_web'\"")]
class CreateCommand : Command
{
readonly ConnectionFeature _connection;
readonly OutputFormatFeature _output;
readonly StoragePathFeature _storagePath;

string? _title, _description, _filter;
readonly List<string> _groups = [];
bool _isProtected;

public CreateCommand()
{
Options.Add(
"t=|title=",
"A title for the view",
t => _title = ArgumentString.Normalize(t));

Options.Add(
"description=",
"A description for the view",
d => _description = ArgumentString.Normalize(d));

Options.Add(
"f=|filter=",
"Expression filter to associate with the view",
f => _filter = ArgumentString.Normalize(f));

Options.Add(
"g=|group=",
"Group key expression to associate with the view; this argument can be used multiple times",
c => _groups.Add(ArgumentString.Normalize(c) ?? throw new ArgumentException("Group keys require a value.")));

Options.Add(
"protected",
"Specify that the view is editable only by administrators",
_ => _isProtected = true);

_connection = Enable<ConnectionFeature>();
_output = Enable<OutputFormatFeature>();
_storagePath = Enable<StoragePathFeature>();
}

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

if (string.IsNullOrEmpty(_title))
{
Log.Error("A title must be specified");
return 1;
}

var view = await connection.Views.TemplateAsync();
view.OwnerId = null;

view.Title = _title;
view.Description = _description;
view.IsProtected = _isProtected;

if (_filter != null)
{
view.Filters = new[]
{
new DescriptiveFilterPart
{
Filter = (await connection.Expressions.ToStrictAsync(_filter)).StrictExpression,
FilterNonStrict = _filter
}
}.ToList();
}

foreach (var group in _groups)
view.GroupKeyExpressions.Add(group);

view = await connection.Views.AddAsync(view);

_output.GetOutputFormat(config).WriteEntity(view);

return 0;
}
}
56 changes: 56 additions & 0 deletions src/SeqCli/Cli/Commands/View/ListCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright 2018 Datalust Pty Ltd and Contributors
//
// 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.Linq;
using System.Threading.Tasks;
using SeqCli.Api;
using SeqCli.Cli.Features;
using SeqCli.Config;

namespace SeqCli.Cli.Commands.View;

[Command("view", "list", "List available metrics views", Example="seqcli view list")]
class ListCommand : Command
{
readonly EntityIdentityFeature _entityIdentity;
readonly ConnectionFeature _connection;
readonly OutputFormatFeature _output;
readonly EntityOwnerFeature _entityOwner;
readonly StoragePathFeature _storagePath;

public ListCommand()
{
_entityIdentity = Enable(new EntityIdentityFeature("view", "list"));
_entityOwner = Enable(new EntityOwnerFeature("view", "list", "listed", _entityIdentity));
_output = Enable<OutputFormatFeature>();
_storagePath = Enable<StoragePathFeature>();
_connection = Enable<ConnectionFeature>();
}

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

var list = _entityIdentity.Id != null ? [await connection.Views.FindAsync(_entityIdentity.Id)]
:
(await connection.Views.ListAsync(ownerId: _entityOwner.OwnerId, shared: _entityOwner.IncludeShared))
.Where(signal => _entityIdentity.Title == null || _entityIdentity.Title == signal.Title);

_output.GetOutputFormat(config).ListEntities(list);

return 0;
}
}
Loading
Loading