├── baseversion ├── dockerfiles └── seqcli │ ├── .gitignore │ ├── linux-arm64.Dockerfile │ └── linux-x64.Dockerfile ├── test ├── SeqCli.EndToEnd │ ├── Data │ │ ├── log-2.txt │ │ ├── log-1.txt │ │ ├── levelled-events.txt │ │ ├── events.ndjson │ │ ├── serilog-events.txt │ │ ├── spans.clef │ │ └── events.clef │ ├── Support │ │ ├── ITestProcess.cs │ │ ├── ICliTestCase.cs │ │ ├── CliTestCaseAttribute.cs │ │ ├── LicenseSetup.cs │ │ └── TestDataFolder.cs │ ├── Bench │ │ └── BenchTestCase.cs │ ├── Diagnostics │ │ └── IngestionLogTestCase.cs │ ├── Properties │ │ └── launchSettings.json │ ├── Node │ │ ├── NodeListTestCase.cs │ │ └── NodeHealthTestCase.cs │ ├── Sample │ │ ├── SampleSetupTestCase.cs │ │ └── SampleIngestTestCase.cs │ ├── SeqCli.EndToEnd.csproj │ ├── Ingest │ │ ├── SimpleTextIngestionTestCase.cs │ │ ├── OverrideLevelIngestionTestCase.cs │ │ ├── NDJsonIngestionTestCase.cs │ │ ├── SpanIngestionTestCase.cs │ │ ├── StrictClefIngestionTestCase.cs │ │ ├── SerilogTextIngestionTestCase.cs │ │ └── LevelledTextIngestionTestCase.cs │ ├── User │ │ ├── UserListTestCase.cs │ │ └── UserCreateRemoveTestCase.cs │ ├── Program.cs │ ├── ApiKey │ │ ├── ApiKeyCreateTestCase.cs │ │ └── ApiKeyDelegatePermissionsTestCase.cs │ ├── License │ │ ├── LicenceShowTestCase.cs │ │ └── LicenseApplyTestsCasecs.cs │ ├── Version │ │ └── VersionCommandTestCase.cs │ ├── Dashboard │ │ └── RenderTestCase.cs │ ├── Signal │ │ ├── SignalCreateTestCase.cs │ │ └── SignalBasicsTestCase.cs │ ├── App │ │ └── AppBasicsTestCase.cs │ ├── Help │ │ └── MarkdownHelpTestCase.cs │ ├── Forwarder │ │ ├── ForwarderIngestionLogTestCase.cs │ │ └── ForwarderSimpleIngestionTestCase.cs │ ├── Args.cs │ ├── Profile │ │ └── ProfileCreateListRemoveTestCase.cs │ ├── Indexes │ │ ├── ExpressionIndexBasicsTestCase.cs │ │ └── IndexesTestCase.cs │ └── Settings │ │ └── SettingBasicsTestCase.cs └── SeqCli.Tests │ ├── Apps │ ├── HealthCheckBinaries │ │ ├── Superpower.dll │ │ ├── Newtonsoft.Json.dll │ │ ├── System.Threading.dll │ │ ├── Seq.Input.HealthCheck.dll │ │ ├── Serilog.Filters.Expressions.dll │ │ ├── System.Collections.NonGeneric.dll │ │ └── Serilog.Formatting.Compact.Reader.dll │ ├── FirstOfTypeBinaries │ │ └── Seq.App.FirstOfType.dll │ ├── AppActivatorTests.cs │ ├── ExampleApp.d.json │ ├── AppLoaderTests.cs │ ├── AppContainerTests.cs │ └── AppDefinitionFormatterTests.cs │ ├── Support │ ├── ActionCommand.cs │ ├── FixedLogEventReader.cs │ ├── Some.cs │ └── TempFolder.cs │ ├── Templates │ ├── test-Expected.template │ └── JsonTemplateParserTests.cs │ ├── Syntax │ └── DurationMonikerTests.cs │ ├── PlainText │ └── StaticMessageTemplateReaderTests.cs │ ├── Cli │ └── NoDuplicateCommands.cs │ ├── Forwarder │ ├── Filesystem │ │ ├── InMemoryStoreFileReader.cs │ │ ├── InMemoryStoreFileAppender.cs │ │ ├── InMemoryStoreFile.cs │ │ └── InMemoryStoreDirectory.cs │ └── Storage │ │ └── IdentifierTests.cs │ ├── Config │ ├── EnvironmentOverridesTests.cs │ └── ConfigFileLocationTests.cs │ ├── Signals │ └── SignalExpressionParserTests.cs │ └── SeqCli.Tests.csproj ├── ci.global.json ├── asset ├── SeqCli.ico └── SeqCli.png └── src ├── SeqCli ├── Properties │ ├── AssemblyInfo.cs │ └── launchSettings.json ├── PlainText │ ├── Patterns │ │ ├── CaptureContentExpression.cs │ │ ├── ExtractionPatternExpression.cs │ │ ├── NonGreedyContentExpression.cs │ │ ├── MatchTypeContentExpression.cs │ │ ├── LiteralTextPatternExpression.cs │ │ ├── CapturePatternExpression.cs │ │ ├── GroupedContentExpression.cs │ │ └── ExtractionPattern.cs │ ├── Extraction │ │ ├── MatcherAttribute.cs │ │ ├── PatternElement.cs │ │ ├── SimplePatternElement.cs │ │ └── NameValueExtractor.cs │ ├── ReifiedProperties.cs │ ├── Parsers │ │ └── SpanEx.cs │ ├── Framing │ │ └── Frame.cs │ └── LogEvents │ │ └── TextOnlyException.cs ├── Ingestion │ ├── ILogEventReader.cs │ ├── TraceConstants.cs │ ├── BatchResult.cs │ ├── ReadResult.cs │ ├── InvalidDataHandling.cs │ ├── EnrichingReader.cs │ ├── BufferingSink.cs │ ├── StaticMessageTemplateReader.cs │ ├── ScalarPropertyEnricher.cs │ └── SendFailureHandling.cs ├── Encryptor │ ├── IDataProtector.cs │ ├── PlaintextDataProtector.cs │ └── WindowsNativeDataProtector.cs ├── Forwarder │ ├── Web │ │ └── Api │ │ │ └── IMapEndpoints.cs │ ├── Util │ │ └── ExecutionEnvironment.cs │ ├── Channel │ │ └── ForwardingChannelEntry.cs │ ├── Storage │ │ ├── BufferReaderChunkExtents.cs │ │ └── BufferAppenderChunk.cs │ ├── Filesystem │ │ ├── EmptyStoreFileReader.cs │ │ ├── StoreFileReader.cs │ │ ├── System │ │ │ └── Unix │ │ │ │ └── Libc.cs │ │ └── StoreFileAppender.cs │ └── Diagnostics │ │ └── InMemorySink.cs ├── Cli │ ├── SupportedPlatforms.cs │ ├── Features │ │ ├── ListenUriFeature.cs │ │ ├── ConfigKeyFeature.cs │ │ ├── BatchSizeFeature.cs │ │ ├── ConfirmFeature.cs │ │ ├── WaitUntilHealthyFeature.cs │ │ ├── DateRangeFeature.cs │ │ ├── InvalidDataHandlingFeature.cs │ │ ├── SendFailureHandlingFeature.cs │ │ ├── ConfigValueFeature.cs │ │ └── SignalExpressionFeature.cs │ ├── ICommandMetadata.cs │ ├── FeatureVisibility.cs │ ├── CommandFeature.cs │ ├── Commands │ │ ├── Profile │ │ │ └── ListCommand.cs │ │ ├── User │ │ │ └── UpdateCommand.cs │ │ ├── Signal │ │ │ └── UpdateCommand.cs │ │ ├── Feed │ │ │ └── UpdateCommand.cs │ │ ├── ApiKey │ │ │ └── UpdateCommand.cs │ │ ├── Workspace │ │ │ └── UpdateCommand.cs │ │ ├── AppInstance │ │ │ ├── UpdateCommand.cs │ │ │ └── ListCommand.cs │ │ ├── Bench │ │ │ ├── BenchCasesCollection.cs │ │ │ └── QueryBenchCase.cs │ │ ├── RetentionPolicy │ │ │ └── UpdateCommand.cs │ │ ├── Diagnostics │ │ │ └── IngestionLogCommand.cs │ │ ├── Settings │ │ │ └── NamesCommand.cs │ │ ├── VersionCommand.cs │ │ ├── ExpressionIndex │ │ │ └── ListCommand.cs │ │ ├── License │ │ │ └── ShowCommand.cs │ │ └── Config │ │ │ ├── ListCommand.cs │ │ │ └── ClearCommand.cs │ ├── CommandMetadata.cs │ ├── Printing.cs │ └── CommandAttribute.cs ├── Csv │ └── CsvToken.cs ├── Sample │ └── Templates │ │ ├── retentionpolicy-Sample Data.template │ │ ├── sqlquery-Order Items by Product Size.template │ │ ├── sqlquery-Route Bindings.template │ │ ├── signal-Bad Request.template │ │ ├── signal-Database.template │ │ ├── signal-Not Found.template │ │ ├── signal-HTTP Requests.template │ │ ├── signal-Order Placed.template │ │ ├── signal-Order Archived.template │ │ ├── signal-Order Created.template │ │ ├── signal-Order Shipped.template │ │ ├── signal-Order Abandoned.template │ │ ├── signal-Success.template │ │ ├── signal-Web Frontend.template │ │ ├── signal-Internal Server Error.template │ │ ├── signal-Batch Processing.template │ │ ├── signal-Information.template │ │ ├── signal-Sample Data.template │ │ ├── signal-Stock Level Warnings.template │ │ └── workspace-Sample.template ├── Config │ ├── Forwarder │ │ ├── SeqCliForwarderApiConfig.cs │ │ ├── SeqCliForwarderStorageConfig.cs │ │ ├── SeqCliForwarderConfig.cs │ │ └── SeqCliForwarderDiagnosticConfig.cs │ ├── SeqCliOutputConfig.cs │ └── RuntimeConfigurationLoader.cs ├── Syntax │ ├── SeqSyntax.cs │ ├── SeqCliNameResolver.cs │ └── CombinedFilterBuilder.cs ├── Util │ ├── Content.cs │ ├── PasswordHash.cs │ ├── TextException.cs │ ├── ArgumentString.cs │ └── LogEventPropertyFactory.cs ├── Templates │ ├── README.md │ ├── Ast │ │ ├── JsonTemplate.cs │ │ ├── JsonTemplateNull.cs │ │ ├── JsonTemplateBoolean.cs │ │ ├── JsonTemplateNumber.cs │ │ ├── JsonTemplateString.cs │ │ ├── JsonTemplateArray.cs │ │ ├── JsonTemplateObject.cs │ │ └── JsonTemplateCall.cs │ ├── Export │ │ └── EntityName.cs │ ├── Evaluator │ │ └── JsonTemplateFunction.cs │ ├── Import │ │ ├── GenericEntity.cs │ │ └── EntityTemplate.cs │ └── Parser │ │ └── JsonTemplateToken.cs ├── Attribution │ ├── System.Reactive License.txt │ ├── Json.NET License.txt │ ├── Autofac License.txt │ └── .NET Core License.txt ├── Api │ └── ApiConstants.cs ├── Output │ ├── StripStructureTypeEnricher.cs │ ├── OutputFormatter.cs │ └── RedundantEventTypeRemovalEnricher.cs ├── Apps │ └── Definitions │ │ ├── AppSettingValue.cs │ │ ├── AppSettingType.cs │ │ └── AppPlatformDefinition.cs ├── Signals │ └── SignalExpressionToken.cs └── SeqCliModule.cs └── Roastery ├── Data ├── IIdentifiable.cs └── DatabaseMigrator.cs ├── Model ├── OrderStatus.cs ├── Order.cs ├── OrderItem.cs └── Product.cs ├── Web ├── HttpServer.cs ├── RouteAttribute.cs ├── HttpResponse.cs ├── NetworkLatencyMiddleware.cs ├── Controller.cs ├── HttpRequest.cs ├── SchedulingLatencyMiddleware.cs └── HttpClient.cs ├── Roastery.csproj ├── Api └── ProductsController.cs ├── Agents └── WarehouseStaff.cs └── Util └── Distribution.cs /baseversion: -------------------------------------------------------------------------------- 1 | 2025.2 -------------------------------------------------------------------------------- /dockerfiles/seqcli/.gitignore: -------------------------------------------------------------------------------- 1 | seqcli.tar.gz -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Data/log-2.txt: -------------------------------------------------------------------------------- 1 | Loop 4 -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Data/log-1.txt: -------------------------------------------------------------------------------- 1 | Loop 1 2 | Loop 2 3 | 4 | Loop 3 5 | -------------------------------------------------------------------------------- /ci.global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "9.0.306" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Data/levelled-events.txt: -------------------------------------------------------------------------------- 1 | INFO Hello 2 | WARN World 3 | -------------------------------------------------------------------------------- /asset/SeqCli.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datalust/seqcli/HEAD/asset/SeqCli.ico -------------------------------------------------------------------------------- /asset/SeqCli.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datalust/seqcli/HEAD/asset/SeqCli.png -------------------------------------------------------------------------------- /src/SeqCli/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | [assembly: InternalsVisibleTo("SeqCli.Tests")] 4 | -------------------------------------------------------------------------------- /src/Roastery/Data/IIdentifiable.cs: -------------------------------------------------------------------------------- 1 | namespace Roastery.Data; 2 | 3 | interface IIdentifiable 4 | { 5 | public string? Id { get; set; } 6 | } -------------------------------------------------------------------------------- /src/Roastery/Model/OrderStatus.cs: -------------------------------------------------------------------------------- 1 | namespace Roastery.Model; 2 | 3 | enum OrderStatus 4 | { 5 | New, 6 | PendingShipment, 7 | Shipped 8 | } -------------------------------------------------------------------------------- /src/SeqCli/PlainText/Patterns/CaptureContentExpression.cs: -------------------------------------------------------------------------------- 1 | namespace SeqCli.PlainText.Patterns; 2 | 3 | abstract class CaptureContentExpression 4 | { 5 | } -------------------------------------------------------------------------------- /src/SeqCli/PlainText/Patterns/ExtractionPatternExpression.cs: -------------------------------------------------------------------------------- 1 | namespace SeqCli.PlainText.Patterns; 2 | 3 | abstract class ExtractionPatternExpression 4 | { 5 | } -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Support/ITestProcess.cs: -------------------------------------------------------------------------------- 1 | namespace SeqCli.EndToEnd.Support; 2 | 3 | public interface ITestProcess 4 | { 5 | string Output { get; } 6 | } -------------------------------------------------------------------------------- /test/SeqCli.Tests/Apps/HealthCheckBinaries/Superpower.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datalust/seqcli/HEAD/test/SeqCli.Tests/Apps/HealthCheckBinaries/Superpower.dll -------------------------------------------------------------------------------- /test/SeqCli.Tests/Apps/HealthCheckBinaries/Newtonsoft.Json.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datalust/seqcli/HEAD/test/SeqCli.Tests/Apps/HealthCheckBinaries/Newtonsoft.Json.dll -------------------------------------------------------------------------------- /test/SeqCli.Tests/Apps/HealthCheckBinaries/System.Threading.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datalust/seqcli/HEAD/test/SeqCli.Tests/Apps/HealthCheckBinaries/System.Threading.dll -------------------------------------------------------------------------------- /test/SeqCli.Tests/Apps/FirstOfTypeBinaries/Seq.App.FirstOfType.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datalust/seqcli/HEAD/test/SeqCli.Tests/Apps/FirstOfTypeBinaries/Seq.App.FirstOfType.dll -------------------------------------------------------------------------------- /test/SeqCli.Tests/Apps/HealthCheckBinaries/Seq.Input.HealthCheck.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datalust/seqcli/HEAD/test/SeqCli.Tests/Apps/HealthCheckBinaries/Seq.Input.HealthCheck.dll -------------------------------------------------------------------------------- /src/SeqCli/Ingestion/ILogEventReader.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace SeqCli.Ingestion; 4 | 5 | interface ILogEventReader 6 | { 7 | Task TryReadAsync(); 8 | } -------------------------------------------------------------------------------- /test/SeqCli.Tests/Apps/HealthCheckBinaries/Serilog.Filters.Expressions.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datalust/seqcli/HEAD/test/SeqCli.Tests/Apps/HealthCheckBinaries/Serilog.Filters.Expressions.dll -------------------------------------------------------------------------------- /test/SeqCli.Tests/Apps/HealthCheckBinaries/System.Collections.NonGeneric.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datalust/seqcli/HEAD/test/SeqCli.Tests/Apps/HealthCheckBinaries/System.Collections.NonGeneric.dll -------------------------------------------------------------------------------- /test/SeqCli.Tests/Apps/HealthCheckBinaries/Serilog.Formatting.Compact.Reader.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datalust/seqcli/HEAD/test/SeqCli.Tests/Apps/HealthCheckBinaries/Serilog.Formatting.Compact.Reader.dll -------------------------------------------------------------------------------- /src/Roastery/Web/HttpServer.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace Roastery.Web; 4 | 5 | abstract class HttpServer 6 | { 7 | public abstract Task InvokeAsync(HttpRequest request); 8 | } -------------------------------------------------------------------------------- /src/SeqCli/Encryptor/IDataProtector.cs: -------------------------------------------------------------------------------- 1 | namespace SeqCli.Encryptor; 2 | 3 | public interface IDataProtector 4 | { 5 | public byte[] Encrypt(byte[] unencrypted); 6 | public byte[] Decrypt(byte[] encrypted); 7 | } -------------------------------------------------------------------------------- /src/SeqCli/Forwarder/Web/Api/IMapEndpoints.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | 3 | namespace SeqCli.Forwarder.Web.Api; 4 | 5 | interface IMapEndpoints 6 | { 7 | void MapEndpoints(WebApplication app); 8 | } -------------------------------------------------------------------------------- /src/SeqCli/Cli/SupportedPlatforms.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SeqCli.Cli; 4 | 5 | [Flags] 6 | public enum SupportedPlatforms 7 | { 8 | None, 9 | Windows, 10 | Posix, 11 | All = Windows | Posix 12 | } -------------------------------------------------------------------------------- /src/SeqCli/Csv/CsvToken.cs: -------------------------------------------------------------------------------- 1 | namespace SeqCli.Csv; 2 | 3 | enum CsvToken 4 | { 5 | None, 6 | Newline, 7 | DoubleQuote, 8 | Comma, 9 | Number, 10 | Boolean, 11 | Null, 12 | Text, 13 | EscapedDoubleQuote 14 | } -------------------------------------------------------------------------------- /src/SeqCli/Ingestion/TraceConstants.cs: -------------------------------------------------------------------------------- 1 | namespace SeqCli.Ingestion; 2 | 3 | static class TraceConstants 4 | { 5 | internal const string ParentSpanIdProperty = "ParentSpanId"; 6 | 7 | internal const string SpanStartTimestampProperty = "SpanStartTimestamp"; 8 | } 9 | -------------------------------------------------------------------------------- /src/SeqCli/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "profiles": { 4 | "SeqCli": { 5 | "commandName": "Project", 6 | "commandLineArgs": "forwarder run --pre" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/SeqCli/Sample/Templates/retentionpolicy-Sample Data.template: -------------------------------------------------------------------------------- 1 | { 2 | "$entity": "retentionpolicy", 3 | "RetentionTime": "30.00:00:00", 4 | "RemovedSignalExpression": { 5 | "SignalId": ref("signal-Sample Data.template"), 6 | "Kind": "Signal" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test/SeqCli.Tests/Support/ActionCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using SeqCli.Cli; 3 | 4 | namespace SeqCli.Tests.Support; 5 | 6 | class ActionCommand : Command 7 | { 8 | public ActionCommand(Action action) 9 | { 10 | action.Invoke(); 11 | } 12 | } -------------------------------------------------------------------------------- /test/SeqCli.Tests/Templates/test-Expected.template: -------------------------------------------------------------------------------- 1 | { 2 | "$entity": "test", 3 | "Name": "Test Stuff", 4 | "ReferencedId": ref("Referenced"), 5 | "Numbers": [ 6 | 1, 7 | 2, 8 | 3 9 | ], 10 | "Dictionary": { 11 | "First": "a" 12 | } 13 | } -------------------------------------------------------------------------------- /src/SeqCli/Config/Forwarder/SeqCliForwarderApiConfig.cs: -------------------------------------------------------------------------------- 1 | namespace SeqCli.Config.Forwarder; 2 | 3 | // ReSharper disable AutoPropertyCanBeMadeGetOnly.Global 4 | 5 | class SeqCliForwarderApiConfig 6 | { 7 | public string ListenUri { get; set; } = "http://127.0.0.1:15341"; 8 | } -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Support/ICliTestCase.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Seq.Api; 3 | using Serilog; 4 | 5 | namespace SeqCli.EndToEnd.Support; 6 | 7 | interface ICliTestCase 8 | { 9 | Task ExecuteAsync(SeqConnection connection, ILogger logger, CliCommandRunner runner); 10 | } -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Data/events.ndjson: -------------------------------------------------------------------------------- 1 | {"Counter":0} 2 | {"Counter":1} 3 | {"Counter":2} 4 | {"Counter":3} 5 | {"Counter":4} 6 | {"Counter":5} 7 | {"Counter":6} 8 | {"Counter":7} 9 | {"Counter":8} 10 | {"Counter":9} 11 | {"Counter":10} 12 | {"Counter":11} 13 | {"Counter":12} 14 | {"Counter":13} 15 | {"Counter":14} -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Support/CliTestCaseAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SeqCli.EndToEnd.Support; 4 | 5 | [AttributeUsage(AttributeTargets.Class)] 6 | public class CliTestCaseAttribute : Attribute 7 | { 8 | public bool Multiuser { get; set; } 9 | public string MinimumApiVersion { get; set; } 10 | } -------------------------------------------------------------------------------- /src/SeqCli/PlainText/Patterns/NonGreedyContentExpression.cs: -------------------------------------------------------------------------------- 1 | namespace SeqCli.PlainText.Patterns; 2 | 3 | class NonGreedyContentExpression : CaptureContentExpression 4 | { 5 | public int Lookahead { get; } 6 | 7 | public NonGreedyContentExpression(int lookahead) 8 | { 9 | Lookahead = lookahead; 10 | } 11 | } -------------------------------------------------------------------------------- /src/SeqCli/Sample/Templates/sqlquery-Order Items by Product Size.template: -------------------------------------------------------------------------------- 1 | { 2 | "$entity": "sqlquery", 3 | "OwnerId": arg("ownerId"), 4 | "Title": "Order Items by Product Size", 5 | "Description": "Created by `seqcli sample setup`", 6 | "Sql": "select count(*) from stream group by Product.SizeInGrams", 7 | "IsProtected": false 8 | } -------------------------------------------------------------------------------- /src/SeqCli/Syntax/SeqSyntax.cs: -------------------------------------------------------------------------------- 1 | using Seq.Syntax.Expressions; 2 | 3 | namespace SeqCli.Syntax; 4 | 5 | static class SeqSyntax 6 | { 7 | public static CompiledExpression CompileExpression(string expression) 8 | { 9 | return SerilogExpression.Compile(expression, nameResolver: new SeqCliNameResolver()); 10 | } 11 | } -------------------------------------------------------------------------------- /src/SeqCli/Encryptor/PlaintextDataProtector.cs: -------------------------------------------------------------------------------- 1 | namespace SeqCli.Encryptor; 2 | 3 | class PlaintextDataProtector : IDataProtector 4 | { 5 | public byte[] Encrypt(byte[] unencrypted) 6 | { 7 | return unencrypted; 8 | } 9 | 10 | public byte[] Decrypt(byte[] encrypted) 11 | { 12 | return encrypted; 13 | } 14 | } -------------------------------------------------------------------------------- /src/SeqCli/Config/Forwarder/SeqCliForwarderStorageConfig.cs: -------------------------------------------------------------------------------- 1 | namespace SeqCli.Config.Forwarder; 2 | 3 | // ReSharper disable AutoPropertyCanBeMadeGetOnly.Global 4 | 5 | public class SeqCliForwarderStorageConfig 6 | { 7 | public long TargetChunkSizeBytes { get; set; } = 100 * 512 * 1024; 8 | public int? MaxChunks { get; set; } = null; 9 | } 10 | -------------------------------------------------------------------------------- /src/SeqCli/Ingestion/BatchResult.cs: -------------------------------------------------------------------------------- 1 | using Serilog.Events; 2 | 3 | namespace SeqCli.Ingestion; 4 | 5 | struct BatchResult 6 | { 7 | public LogEvent[] LogEvents { get; } 8 | public bool IsLast { get; } 9 | 10 | public BatchResult(LogEvent[] logEvents, bool isLast) 11 | { 12 | LogEvents = logEvents; 13 | IsLast = isLast; 14 | } 15 | } -------------------------------------------------------------------------------- /src/SeqCli/Util/Content.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace SeqCli.Util; 5 | 6 | static class Content 7 | { 8 | public static string GetPath(string relativePath) 9 | { 10 | var thisDir = Path.GetDirectoryName(Path.GetFullPath(AppContext.BaseDirectory)) ?? "."; 11 | return Path.Combine(thisDir, relativePath); 12 | } 13 | } -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Data/serilog-events.txt: -------------------------------------------------------------------------------- 1 | 2018-07-06 09:02:17.148 +10:00 [INF] HTTP GET / responded 200 in 3 ms 2 | 2018-07-06 09:02:18.259 +10:00 [ERR] HTTP GET / responded 500 in 12345 ms 3 | System.InvalidOperationException: this is a test 4 | at MyClass.MyFlakyMethod() in SomeFile.cs:line 200 5 | 2018-07-06 09:02:19.360 +10:00 [INF] HTTP GET / responded 200 in 4 ms 6 | -------------------------------------------------------------------------------- /src/SeqCli/Ingestion/ReadResult.cs: -------------------------------------------------------------------------------- 1 | using Serilog.Events; 2 | 3 | namespace SeqCli.Ingestion; 4 | 5 | readonly struct ReadResult 6 | { 7 | public LogEvent? LogEvent { get; } 8 | public bool IsAtEnd { get; } 9 | 10 | public ReadResult(LogEvent? logEvent, bool isAtEnd) 11 | { 12 | LogEvent = logEvent; 13 | IsAtEnd = isAtEnd; 14 | } 15 | } -------------------------------------------------------------------------------- /src/SeqCli/PlainText/Patterns/MatchTypeContentExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SeqCli.PlainText.Patterns; 4 | 5 | class MatchTypeContentExpression : CaptureContentExpression 6 | { 7 | public string Type { get; } 8 | 9 | public MatchTypeContentExpression(string type) 10 | { 11 | Type = type ?? throw new ArgumentNullException(nameof(type)); 12 | } 13 | } -------------------------------------------------------------------------------- /src/Roastery/Web/RouteAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Roastery.Web; 4 | 5 | [AttributeUsage(AttributeTargets.Method)] 6 | class RouteAttribute : Attribute 7 | { 8 | public string Method { get; } 9 | public string Path { get; } 10 | 11 | public RouteAttribute(string method, string path) 12 | { 13 | Method = method; 14 | Path = path; 15 | } 16 | } -------------------------------------------------------------------------------- /src/SeqCli/PlainText/Extraction/MatcherAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SeqCli.PlainText.Extraction; 4 | 5 | [AttributeUsage(AttributeTargets.Property)] 6 | class MatcherAttribute : Attribute 7 | { 8 | public string Name { get; } 9 | 10 | public MatcherAttribute(string name) 11 | { 12 | Name = name ?? throw new ArgumentNullException(nameof(name)); 13 | } 14 | } -------------------------------------------------------------------------------- /src/SeqCli/PlainText/Patterns/LiteralTextPatternExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SeqCli.PlainText.Patterns; 4 | 5 | class LiteralTextPatternExpression : ExtractionPatternExpression 6 | { 7 | public string Text { get; } 8 | 9 | public LiteralTextPatternExpression(string text) 10 | { 11 | Text = text ?? throw new ArgumentNullException(nameof(text)); 12 | } 13 | } -------------------------------------------------------------------------------- /src/SeqCli/Sample/Templates/sqlquery-Route Bindings.template: -------------------------------------------------------------------------------- 1 | { 2 | "$entity": "sqlquery", 3 | "OwnerId": arg("ownerId"), 4 | "Title": "Route Bindings", 5 | "Description": "Created by `seqcli sample setup`", 6 | "Sql": "select first(Controller) as controller, first(Action) as action\nfrom stream\nwhere @EventType = 0x8E9D69C7 -- route bindings\ngroup by HttpMethod, RouteTemplate", 7 | "IsProtected": false 8 | } -------------------------------------------------------------------------------- /src/Roastery/Roastery.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/SeqCli/PlainText/Patterns/CapturePatternExpression.cs: -------------------------------------------------------------------------------- 1 | namespace SeqCli.PlainText.Patterns; 2 | 3 | class CapturePatternExpression : ExtractionPatternExpression 4 | { 5 | public string Name { get; } 6 | public CaptureContentExpression? Content { get; } 7 | 8 | public CapturePatternExpression(string name, CaptureContentExpression? content) 9 | { 10 | Name = name; 11 | Content = content; 12 | } 13 | } -------------------------------------------------------------------------------- /src/SeqCli/Cli/Features/ListenUriFeature.cs: -------------------------------------------------------------------------------- 1 | namespace SeqCli.Cli.Features; 2 | 3 | class ListenUriFeature : CommandFeature 4 | { 5 | public string? ListenUri { get; private set; } 6 | 7 | public override void Enable(OptionSet options) 8 | { 9 | options.Add("l=|listen=", 10 | "Set the address `seqcli forwarder` will listen at; http://127.0.0.1:15341/ is used by default.", 11 | v => ListenUri = v); 12 | } 13 | } -------------------------------------------------------------------------------- /src/SeqCli/PlainText/Patterns/GroupedContentExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SeqCli.PlainText.Patterns; 4 | 5 | class GroupedContentExpression : CaptureContentExpression 6 | { 7 | public ExtractionPattern ExtractionPattern { get; } 8 | 9 | public GroupedContentExpression(ExtractionPattern extractionPattern) 10 | { 11 | ExtractionPattern = extractionPattern ?? throw new ArgumentNullException(nameof(extractionPattern)); 12 | } 13 | } -------------------------------------------------------------------------------- /src/SeqCli/Config/Forwarder/SeqCliForwarderConfig.cs: -------------------------------------------------------------------------------- 1 | namespace SeqCli.Config.Forwarder; 2 | 3 | // ReSharper disable AutoPropertyCanBeMadeGetOnly.Global 4 | 5 | class SeqCliForwarderConfig 6 | { 7 | public SeqCliForwarderStorageConfig Storage { get; set; } = new(); 8 | public SeqCliForwarderDiagnosticConfig Diagnostics { get; set; } = new(); 9 | public SeqCliForwarderApiConfig Api { get; set; } = new(); 10 | public bool UseApiKeyForwarding { get; set; } 11 | } -------------------------------------------------------------------------------- /test/SeqCli.Tests/Support/FixedLogEventReader.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using SeqCli.Ingestion; 3 | 4 | namespace SeqCli.Tests.Support; 5 | 6 | class FixedLogEventReader : ILogEventReader 7 | { 8 | readonly ReadResult _result; 9 | 10 | public FixedLogEventReader(ReadResult result) 11 | { 12 | _result = result; 13 | } 14 | 15 | public Task TryReadAsync() 16 | { 17 | return Task.FromResult(_result); 18 | } 19 | } -------------------------------------------------------------------------------- /test/SeqCli.Tests/Apps/AppActivatorTests.cs: -------------------------------------------------------------------------------- 1 | using SeqCli.Apps.Hosting; 2 | using Xunit; 3 | 4 | namespace SeqCli.Tests.Apps; 5 | 6 | public class AppActivatorTests 7 | { 8 | enum Test 9 | { 10 | First, 11 | Second 12 | } 13 | 14 | [Fact] 15 | public void CanConvertStringsToEnumValues() 16 | { 17 | var converted = AppActivator.ConvertToSettingType("Second", typeof(Test)); 18 | Assert.Equal(Test.Second, converted); 19 | } 20 | } -------------------------------------------------------------------------------- /src/Roastery/Model/Order.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Roastery.Data; 3 | 4 | namespace Roastery.Model; 5 | 6 | class Order : IIdentifiable 7 | { 8 | public string? Id { get; set; } 9 | 10 | public DateTime CreatedAt { get; set; } = DateTime.UtcNow; 11 | 12 | // Nullable so we can emulate invalid requests :-) 13 | public string? CustomerName { get; set; } 14 | public string? ShippingAddress { get; set; } 15 | 16 | public OrderStatus Status { get; set; } 17 | } -------------------------------------------------------------------------------- /src/SeqCli/PlainText/Patterns/ExtractionPattern.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace SeqCli.PlainText.Patterns; 6 | 7 | class ExtractionPattern 8 | { 9 | public IReadOnlyList Elements { get; } 10 | 11 | public ExtractionPattern(IEnumerable items) 12 | { 13 | if (items == null) throw new ArgumentNullException(nameof(items)); 14 | Elements = items.ToArray(); 15 | } 16 | } -------------------------------------------------------------------------------- /src/SeqCli/Sample/Templates/signal-Bad Request.template: -------------------------------------------------------------------------------- 1 | { 2 | "$entity": "signal", 3 | "OwnerId": arg("ownerId"), 4 | "Title": "Bad Request", 5 | "Description": "Created by `seqcli sample setup`", 6 | "Filters": [ 7 | { 8 | "Description": null, 9 | "DescriptionIsExcluded": false, 10 | "Filter": "StatusCode = 400", 11 | "FilterNonStrict": null 12 | } 13 | ], 14 | "Columns": [], 15 | "IsProtected": false, 16 | "Grouping": "Inferred", 17 | "ExplicitGroupName": null 18 | } -------------------------------------------------------------------------------- /src/SeqCli/Sample/Templates/signal-Database.template: -------------------------------------------------------------------------------- 1 | { 2 | "$entity": "signal", 3 | "OwnerId": arg("ownerId"), 4 | "Title": "Database", 5 | "Description": "Created by `seqcli sample setup`", 6 | "Filters": [ 7 | { 8 | "Description": null, 9 | "DescriptionIsExcluded": false, 10 | "Filter": "@EventType = 0x94002ebf", 11 | "FilterNonStrict": null 12 | } 13 | ], 14 | "Columns": [], 15 | "IsProtected": false, 16 | "Grouping": "None", 17 | "ExplicitGroupName": null 18 | } -------------------------------------------------------------------------------- /src/SeqCli/Sample/Templates/signal-Not Found.template: -------------------------------------------------------------------------------- 1 | { 2 | "$entity": "signal", 3 | "OwnerId": arg("ownerId"), 4 | "Title": "Not Found", 5 | "Description": "Created by `seqcli sample setup`", 6 | "Filters": [ 7 | { 8 | "Description": null, 9 | "DescriptionIsExcluded": false, 10 | "Filter": "StatusCode = 404", 11 | "FilterNonStrict": null 12 | } 13 | ], 14 | "Columns": [], 15 | "IsProtected": false, 16 | "Grouping": "Inferred", 17 | "ExplicitGroupName": null 18 | } -------------------------------------------------------------------------------- /src/SeqCli/Sample/Templates/signal-HTTP Requests.template: -------------------------------------------------------------------------------- 1 | { 2 | "$entity": "signal", 3 | "OwnerId": arg("ownerId"), 4 | "Title": "HTTP Requests", 5 | "Description": "Created by `seqcli sample setup`", 6 | "Filters": [ 7 | { 8 | "Description": null, 9 | "DescriptionIsExcluded": false, 10 | "Filter": "@EventType = 0x672bd10e", 11 | "FilterNonStrict": null 12 | } 13 | ], 14 | "Columns": [], 15 | "IsProtected": false, 16 | "Grouping": "None", 17 | "ExplicitGroupName": null 18 | } -------------------------------------------------------------------------------- /src/SeqCli/Sample/Templates/signal-Order Placed.template: -------------------------------------------------------------------------------- 1 | { 2 | "$entity": "signal", 3 | "OwnerId": arg("ownerId"), 4 | "Title": "Order Placed", 5 | "Description": "Created by `seqcli sample setup`", 6 | "Filters": [ 7 | { 8 | "Description": null, 9 | "DescriptionIsExcluded": false, 10 | "Filter": "@EventType = 0xece21a0a", 11 | "FilterNonStrict": null 12 | } 13 | ], 14 | "Columns": [], 15 | "IsProtected": false, 16 | "Grouping": "Explicit", 17 | "ExplicitGroupName": "Ordering" 18 | } -------------------------------------------------------------------------------- /src/SeqCli/Sample/Templates/signal-Order Archived.template: -------------------------------------------------------------------------------- 1 | { 2 | "$entity": "signal", 3 | "OwnerId": arg("ownerId"), 4 | "Title": "Order Archived", 5 | "Description": "Created by `seqcli sample setup`", 6 | "Filters": [ 7 | { 8 | "Description": null, 9 | "DescriptionIsExcluded": false, 10 | "Filter": "@EventType = 0xbf71696d", 11 | "FilterNonStrict": null 12 | } 13 | ], 14 | "Columns": [], 15 | "IsProtected": false, 16 | "Grouping": "Explicit", 17 | "ExplicitGroupName": "Ordering" 18 | } -------------------------------------------------------------------------------- /src/SeqCli/Sample/Templates/signal-Order Created.template: -------------------------------------------------------------------------------- 1 | { 2 | "$entity": "signal", 3 | "OwnerId": arg("ownerId"), 4 | "Title": "Order Created", 5 | "Description": "Created by `seqcli sample setup`", 6 | "Filters": [ 7 | { 8 | "Description": null, 9 | "DescriptionIsExcluded": false, 10 | "Filter": "@EventType = 0x25c09546", 11 | "FilterNonStrict": null 12 | } 13 | ], 14 | "Columns": [], 15 | "IsProtected": false, 16 | "Grouping": "Explicit", 17 | "ExplicitGroupName": "Ordering" 18 | } -------------------------------------------------------------------------------- /src/SeqCli/Sample/Templates/signal-Order Shipped.template: -------------------------------------------------------------------------------- 1 | { 2 | "$entity": "signal", 3 | "OwnerId": arg("ownerId"), 4 | "Title": "Order Shipped", 5 | "Description": "Created by `seqcli sample setup`", 6 | "Filters": [ 7 | { 8 | "Description": null, 9 | "DescriptionIsExcluded": false, 10 | "Filter": "@EventType = 0x8cc54029", 11 | "FilterNonStrict": null 12 | } 13 | ], 14 | "Columns": [], 15 | "IsProtected": false, 16 | "Grouping": "Explicit", 17 | "ExplicitGroupName": "Ordering" 18 | } -------------------------------------------------------------------------------- /src/SeqCli/Sample/Templates/signal-Order Abandoned.template: -------------------------------------------------------------------------------- 1 | { 2 | "$entity": "signal", 3 | "OwnerId": arg("ownerId"), 4 | "Title": "Order Abandoned", 5 | "Description": "Created by `seqcli sample setup`", 6 | "Filters": [ 7 | { 8 | "Description": null, 9 | "DescriptionIsExcluded": false, 10 | "Filter": "@EventType = 0x0c664bf4", 11 | "FilterNonStrict": null 12 | } 13 | ], 14 | "Columns": [], 15 | "IsProtected": false, 16 | "Grouping": "Explicit", 17 | "ExplicitGroupName": "Ordering" 18 | } -------------------------------------------------------------------------------- /src/SeqCli/Sample/Templates/signal-Success.template: -------------------------------------------------------------------------------- 1 | { 2 | "$entity": "signal", 3 | "OwnerId": arg("ownerId"), 4 | "Title": "Success", 5 | "Description": "Created by `seqcli sample setup`", 6 | "Filters": [ 7 | { 8 | "Description": null, 9 | "DescriptionIsExcluded": false, 10 | "Filter": "StatusCode >= 200 and StatusCode < 300", 11 | "FilterNonStrict": null 12 | } 13 | ], 14 | "Columns": [], 15 | "IsProtected": false, 16 | "Grouping": "Explicit", 17 | "ExplicitGroupName": "StatusCode" 18 | } -------------------------------------------------------------------------------- /src/SeqCli/Sample/Templates/signal-Web Frontend.template: -------------------------------------------------------------------------------- 1 | { 2 | "$entity": "signal", 3 | "OwnerId": arg("ownerId"), 4 | "Title": "Web Frontend", 5 | "Description": "Created by `seqcli signal setup`", 6 | "Filters": [ 7 | { 8 | "Description": null, 9 | "DescriptionIsExcluded": false, 10 | "Filter": "Application = 'Roastery Web Frontend'", 11 | "FilterNonStrict": null 12 | } 13 | ], 14 | "Columns": [], 15 | "IsProtected": false, 16 | "Grouping": "Inferred", 17 | "ExplicitGroupName": null 18 | } -------------------------------------------------------------------------------- /src/SeqCli/Sample/Templates/signal-Internal Server Error.template: -------------------------------------------------------------------------------- 1 | { 2 | "$entity": "signal", 3 | "OwnerId": arg("ownerId"), 4 | "Title": "Internal Server Error", 5 | "Description": "Created by `seqcli sample setup`", 6 | "Filters": [ 7 | { 8 | "Description": null, 9 | "DescriptionIsExcluded": false, 10 | "Filter": "StatusCode = 500", 11 | "FilterNonStrict": null 12 | } 13 | ], 14 | "Columns": [], 15 | "IsProtected": false, 16 | "Grouping": "Explicit", 17 | "ExplicitGroupName": "StatusCode" 18 | } -------------------------------------------------------------------------------- /src/SeqCli/Sample/Templates/signal-Batch Processing.template: -------------------------------------------------------------------------------- 1 | { 2 | "$entity": "signal", 3 | "OwnerId": arg("ownerId"), 4 | "Title": "Batch Processing", 5 | "Description": "Created by `seqcli sample setup`", 6 | "Filters": [ 7 | { 8 | "Description": null, 9 | "DescriptionIsExcluded": false, 10 | "Filter": "Application = 'Roastery Batch Processing'", 11 | "FilterNonStrict": null 12 | } 13 | ], 14 | "Columns": [], 15 | "IsProtected": false, 16 | "Grouping": "Inferred", 17 | "ExplicitGroupName": null 18 | } -------------------------------------------------------------------------------- /src/SeqCli/Sample/Templates/signal-Information.template: -------------------------------------------------------------------------------- 1 | { 2 | "$entity": "signal", 3 | "OwnerId": arg("ownerId"), 4 | "Title": "Information", 5 | "Description": "Created by `seqcli sample setup`", 6 | "Filters": [ 7 | { 8 | "Description": null, 9 | "DescriptionIsExcluded": false, 10 | "Filter": "@Level in ['inf', 'info', 'information'] ci", 11 | "FilterNonStrict": null 12 | } 13 | ], 14 | "Columns": [], 15 | "IsProtected": false, 16 | "Grouping": "Explicit", 17 | "ExplicitGroupName": "@Level" 18 | } -------------------------------------------------------------------------------- /dockerfiles/seqcli/linux-arm64.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 2 | 3 | RUN apt-get update \ 4 | && apt-get install -y --no-install-recommends \ 5 | ca-certificates \ 6 | libc6 \ 7 | libgcc1 \ 8 | libgssapi-krb5-2 \ 9 | libicu70 \ 10 | libssl3 \ 11 | libstdc++6 \ 12 | zlib1g \ 13 | && rm -rf /var/lib/apt/lists/* 14 | 15 | COPY src/SeqCli/bin/Release/net9.0/linux-arm64/publish /bin/seqcli 16 | 17 | ENTRYPOINT ["/bin/seqcli/seqcli"] 18 | 19 | LABEL Description="seqcli" Vendor="Datalust Pty Ltd" 20 | -------------------------------------------------------------------------------- /dockerfiles/seqcli/linux-x64.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 2 | 3 | RUN apt-get update \ 4 | && apt-get install -y --no-install-recommends \ 5 | ca-certificates \ 6 | libc6 \ 7 | libgcc1 \ 8 | libgssapi-krb5-2 \ 9 | libicu70 \ 10 | libssl3 \ 11 | libstdc++6 \ 12 | zlib1g \ 13 | && rm -rf /var/lib/apt/lists/* 14 | 15 | COPY src/SeqCli/bin/Release/net9.0/linux-x64/publish /bin/seqcli 16 | 17 | ENTRYPOINT ["/bin/seqcli/seqcli"] 18 | 19 | LABEL Description="seqcli" Vendor="Datalust Pty Ltd" 20 | -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Bench/BenchTestCase.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Seq.Api; 3 | using SeqCli.EndToEnd.Support; 4 | using Serilog; 5 | using Xunit; 6 | 7 | namespace SeqCli.EndToEnd.Bench; 8 | 9 | public class BenchTestCase : ICliTestCase 10 | { 11 | public Task ExecuteAsync( 12 | SeqConnection connection, 13 | ILogger logger, 14 | CliCommandRunner runner) 15 | { 16 | var exit = runner.Exec("bench", "--runs 3 --with-queries"); 17 | Assert.Equal(0, exit); 18 | 19 | return Task.CompletedTask; 20 | } 21 | } -------------------------------------------------------------------------------- /src/SeqCli/Sample/Templates/signal-Sample Data.template: -------------------------------------------------------------------------------- 1 | { 2 | "$entity": "signal", 3 | "Title": "Sample Data", 4 | "Description": "Created by `seqcli sample setup`", 5 | "Filters": [ 6 | { 7 | "Description": null, 8 | "DescriptionIsExcluded": false, 9 | "Filter": "Origin = 'seqcli sample ingest'", 10 | "FilterNonStrict": "Origin = 'seqcli sample ingest'" 11 | } 12 | ], 13 | "Columns": [], 14 | "IsProtected": false, 15 | "Grouping": "Inferred", 16 | "ExplicitGroupName": null, 17 | "OwnerId": null 18 | } 19 | -------------------------------------------------------------------------------- /src/SeqCli/Sample/Templates/signal-Stock Level Warnings.template: -------------------------------------------------------------------------------- 1 | { 2 | "$entity": "signal", 3 | "OwnerId": arg("ownerId"), 4 | "Title": "Stock Level Warnings", 5 | "Description": "Created by `seqcli sample setup`", 6 | "Filters": [ 7 | { 8 | "Description": null, 9 | "DescriptionIsExcluded": false, 10 | "Filter": "@EventType in [4029526114, 1918830704]", 11 | "FilterNonStrict": "@EventType in [0xF02DB062, 0x725F0870]" 12 | } 13 | ], 14 | "Columns": [], 15 | "IsProtected": false, 16 | "Grouping": "Inferred", 17 | "ExplicitGroupName": null 18 | } -------------------------------------------------------------------------------- /src/Roastery/Model/OrderItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Roastery.Data; 3 | 4 | namespace Roastery.Model; 5 | 6 | public class OrderItem: IIdentifiable 7 | { 8 | public string? Id { get; set; } 9 | public string OrderId { get; set; } 10 | public string ProductId { get; set; } 11 | 12 | [Obsolete("Serialization constructor.")] 13 | #pragma warning disable 8618 14 | public OrderItem() { } 15 | #pragma warning restore 8618 16 | 17 | public OrderItem(string orderId, string productId) 18 | { 19 | OrderId = orderId; 20 | ProductId = productId; 21 | } 22 | } -------------------------------------------------------------------------------- /src/SeqCli/Syntax/SeqCliNameResolver.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | using Seq.Syntax.Expressions; 3 | 4 | namespace SeqCli.Syntax; 5 | 6 | class SeqCliNameResolver: NameResolver 7 | { 8 | public override bool TryResolveBuiltInPropertyName(string alias, [MaybeNullWhen(false)] out string target) 9 | { 10 | switch (alias) 11 | { 12 | case "@l": 13 | target = "coalesce(SeqCliOriginalLevel, @l)"; 14 | return true; 15 | default: 16 | target = null; 17 | return false; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/SeqCli/Config/Forwarder/SeqCliForwarderDiagnosticConfig.cs: -------------------------------------------------------------------------------- 1 | using Serilog.Events; 2 | // ReSharper disable UnusedAutoPropertyAccessor.Global 3 | // ReSharper disable AutoPropertyCanBeMadeGetOnly.Global 4 | 5 | namespace SeqCli.Config.Forwarder; 6 | 7 | class SeqCliForwarderDiagnosticConfig 8 | { 9 | public LogEventLevel InternalLoggingLevel { get; set; } = LogEventLevel.Information; 10 | public string? InternalLogServerUri { get; set; } 11 | public string? InternalLogServerApiKey { get; set; } 12 | public bool ExposeIngestionLog { get; set; } 13 | public bool IngestionLogShowDetail { get; set; } 14 | } 15 | -------------------------------------------------------------------------------- /src/Roastery/Web/HttpResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Net.Http; 3 | 4 | namespace Roastery.Web; 5 | 6 | class HttpResponse 7 | { 8 | public HttpStatusCode StatusCode { get; } 9 | public object? Body { get; } 10 | 11 | public HttpResponse(HttpStatusCode statusCode, object? body = null) 12 | { 13 | StatusCode = statusCode; 14 | Body = body; 15 | } 16 | 17 | public void EnsureSuccessStatusCode() 18 | { 19 | if ((int) StatusCode >= 400) 20 | throw new HttpRequestException($"Request failed with status code {(int) StatusCode}/{StatusCode}."); 21 | } 22 | } -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Diagnostics/IngestionLogTestCase.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Seq.Api; 3 | using SeqCli.EndToEnd.Support; 4 | using Serilog; 5 | using Xunit; 6 | 7 | namespace SeqCli.EndToEnd.Diagnostics; 8 | 9 | public class IngestionLogTestCase: ICliTestCase 10 | { 11 | public Task ExecuteAsync(SeqConnection connection, ILogger logger, CliCommandRunner runner) 12 | { 13 | var exit = runner.Exec("diagnostics ingestionlog"); 14 | Assert.Equal(0, exit); 15 | 16 | Assert.StartsWith("[20", runner.LastRunProcess!.Output); 17 | 18 | return Task.CompletedTask; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/SeqCli/Cli/Features/ConfigKeyFeature.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using SeqCli.Util; 3 | 4 | namespace SeqCli.Cli.Features; 5 | 6 | class ConfigKeyFeature: CommandFeature 7 | { 8 | string? _key; 9 | 10 | public override void Enable(OptionSet options) 11 | { 12 | options.Add("k|key=", "The field, for example `connection.serverUrl`", k => _key = ArgumentString.Normalize(k)); 13 | } 14 | 15 | public string GetKey() 16 | { 17 | if (string.IsNullOrEmpty(_key)) 18 | throw new ArgumentException("A field must be supplied with `--key=KEY`."); 19 | 20 | return _key; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Data/spans.clef: -------------------------------------------------------------------------------- 1 | {"@t":"2023-12-20T00:55:07Z","@st":"2023-12-20T00:50:07Z","@tr":"af2f559d8ac24a288d2aaef55439778b","@sp":"5b08fd4ac3aa42d4","@ps":"44acf182dec544fb","@ra":{"a":1},"@sa":{"b":1},"@m":"My info log"} 2 | {"@l":"Warning","@t":"2023-12-20T00:55:07Z","@st":"2023-12-20T00:50:07Z","@tr":"af2f559d8ac24a288d2aaef55439778b","@sp":"5b08fd4ac3aa42d4","@ps":"44acf182dec544fb","@ra":{"a":1},"@sa":{"b":1},"@m":"My warning log"} 3 | {"@l":"ERR","@t":"2023-12-20T00:55:07Z","@st":"2023-12-20T00:50:07Z","@tr":"af2f559d8ac24a288d2aaef55439778b","@sp":"5b08fd4ac3aa42d4","@ps":"44acf182dec544fb","@ra":{"a":1},"@sa":{"b":1},"@m":"My error log"} 4 | -------------------------------------------------------------------------------- /src/SeqCli/Forwarder/Util/ExecutionEnvironment.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace SeqCli.Forwarder.Util; 4 | 5 | static class ExecutionEnvironment 6 | { 7 | public static bool SupportsStandardIO => !IsRunningAsWindowsService; 8 | 9 | static bool IsRunningAsWindowsService 10 | { 11 | get 12 | { 13 | if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) 14 | { 15 | var parent = WindowsProcess.GetParentProcess(); 16 | return parent?.ProcessName == "services"; 17 | } 18 | 19 | return false; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/SeqCli/PlainText/ReifiedProperties.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace SeqCli.PlainText; 4 | 5 | static class ReifiedProperties 6 | { 7 | public const string 8 | Message = "@m", 9 | Timestamp = "@t", 10 | Level = "@l", 11 | Exception = "@x", 12 | StartTimestamp = "@st", 13 | SpanId = "@sp", 14 | TraceId = "@tr"; 15 | 16 | static readonly HashSet All = [Message, Timestamp, Level, Exception, StartTimestamp, SpanId, TraceId]; 17 | 18 | public static bool IsReifiedProperty(string name) 19 | { 20 | return All.Contains(name); 21 | } 22 | } -------------------------------------------------------------------------------- /src/Roastery/Model/Product.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Roastery.Data; 3 | 4 | namespace Roastery.Model; 5 | 6 | class Product: IIdentifiable 7 | { 8 | public string? Id { get; set; } 9 | public string Name { get; set; } 10 | public int SizeInGrams { get; set; } 11 | 12 | public string FormatDescription() => $"{Name} {SizeInGrams}g"; 13 | 14 | [Obsolete("Serialization constructor.")] 15 | #pragma warning disable 8618 16 | public Product() { } 17 | #pragma warning restore 8618 18 | 19 | public Product(string name, int sizeInGrams) 20 | { 21 | Name = name; 22 | SizeInGrams = sizeInGrams; 23 | } 24 | } -------------------------------------------------------------------------------- /src/SeqCli/Templates/README.md: -------------------------------------------------------------------------------- 1 | # SeqCli.Templates 2 | 3 | A simple JSON templating system, designed for importing sets of 4 | related entities into a Seq server. 5 | 6 | ## Template syntax 7 | 8 | The templating language is a JSON superset which, when evaluated, 9 | produces JSON values. 10 | 11 | The syntax extends JSON with JavaScript-style function calls: 12 | 13 | ``` 14 | { 15 | "Title": "Web", 16 | "SignalId": ref("signal-HTTP Requests.json") 17 | } 18 | ``` 19 | 20 | The supported functions are: 21 | 22 | * `ref(filename)` - resolves to the id of the entity produced 23 | by importing the template _filename_ into the target Seq instance. 24 | 25 | -------------------------------------------------------------------------------- /src/Roastery/Web/NetworkLatencyMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Roastery.Util; 3 | 4 | namespace Roastery.Web; 5 | 6 | class NetworkLatencyMiddleware : HttpServer 7 | { 8 | readonly HttpServer _next; 9 | 10 | public NetworkLatencyMiddleware(HttpServer next) 11 | { 12 | _next = next; 13 | } 14 | 15 | public override async Task InvokeAsync(HttpRequest request) 16 | { 17 | await Task.Delay(100 + (int)(Distribution.Uniform() * 300)); 18 | var response = await _next.InvokeAsync(request); 19 | await Task.Delay(10 + (int) (Distribution.Uniform() * 100)); 20 | return response; 21 | } 22 | } -------------------------------------------------------------------------------- /src/SeqCli/Attribution/System.Reactive License.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) .NET Foundation and Contributors 2 | All Rights Reserved 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); you 5 | may not use this file except in compliance with the License. You may 6 | obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | implied. See the License for the specific language governing permissions 14 | and limitations under the License. 15 | -------------------------------------------------------------------------------- /src/Roastery/Api/ProductsController.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Roastery.Data; 3 | using Roastery.Model; 4 | using Roastery.Web; 5 | using Serilog; 6 | 7 | // ReSharper disable UnusedMember.Global 8 | 9 | namespace Roastery.Api; 10 | 11 | class ProductsController : Controller 12 | { 13 | readonly Database _database; 14 | 15 | public ProductsController(ILogger logger, Database database) 16 | : base(logger) 17 | { 18 | _database = database; 19 | } 20 | 21 | [Route("GET", "api/products")] 22 | public async Task ListAsync(HttpRequest request) 23 | { 24 | return Json(await _database.SelectAsync()); 25 | } 26 | } -------------------------------------------------------------------------------- /test/SeqCli.Tests/Syntax/DurationMonikerTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using SeqCli.Syntax; 3 | using Xunit; 4 | 5 | namespace SeqCli.Tests.Syntax; 6 | 7 | public class DurationMonikerTests 8 | { 9 | [Theory] 10 | [InlineData("1d", 1000L * 60 * 60 * 24)] 11 | [InlineData("7d", 1000L * 60 * 60 * 24 * 7)] 12 | [InlineData("30ms", 30L)] 13 | public void MonikersAreParsedAndFormatted(string duration, long equivalentMilliseconds) 14 | { 15 | var ts = DurationMoniker.ToTimeSpan(duration); 16 | Assert.Equal(TimeSpan.FromMilliseconds(equivalentMilliseconds), ts); 17 | 18 | var dm = DurationMoniker.FromTimeSpan(ts); 19 | Assert.Equal(duration, dm); 20 | } 21 | } -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "profiles": { 4 | "SeqCli.EndToEnd (Seq Executable)": { 5 | "commandName": "Project" 6 | }, 7 | "SeqCli.EndToEnd (datalust/seq:latest)": { 8 | "commandName": "Project", 9 | "commandLineArgs": "--docker-server" 10 | }, 11 | "SeqCli.EndToEnd (datalust/seq:latest, podman)": { 12 | "commandName": "Project", 13 | "commandLineArgs": "--docker-server --podman" 14 | }, 15 | "SeqCli.EndToEnd (datalust/seq:preview)": { 16 | "commandName": "Project", 17 | "commandLineArgs": "--docker-server --pre" 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Node/NodeListTestCase.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Seq.Api; 3 | using SeqCli.EndToEnd.Support; 4 | using Serilog; 5 | using Xunit; 6 | 7 | namespace SeqCli.EndToEnd.Node; 8 | 9 | [CliTestCase] 10 | public class NodeListTestCase: ICliTestCase 11 | { 12 | public Task ExecuteAsync(SeqConnection connection, ILogger logger, CliCommandRunner runner) 13 | { 14 | var exit = runner.Exec("node list --json"); 15 | Assert.Equal(0, exit); 16 | 17 | Assert.Contains("\"Name\":", runner.LastRunProcess!.Output); 18 | Assert.Contains("\"ClusterListenUri\":", runner.LastRunProcess!.Output); 19 | return Task.CompletedTask; 20 | } 21 | } -------------------------------------------------------------------------------- /src/SeqCli/Cli/Features/BatchSizeFeature.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using SeqCli.Util; 3 | 4 | namespace SeqCli.Cli.Features; 5 | 6 | class BatchSizeFeature: CommandFeature 7 | { 8 | const int DefaultBatchSize = 100; 9 | int? _value; 10 | 11 | public int Value => _value ?? DefaultBatchSize; 12 | 13 | public override void Enable(OptionSet options) 14 | { 15 | options.Add("batch-size=", 16 | $"The maximum number of events to send in each request to the ingestion endpoint; if not specified a value of `{DefaultBatchSize}` will be used", 17 | v => _value = int.Parse(ArgumentString.Normalize(v) ?? throw new ArgumentException("Batch size requires an argument."))); 18 | } 19 | } -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Sample/SampleSetupTestCase.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Threading.Tasks; 3 | using Seq.Api; 4 | using SeqCli.EndToEnd.Support; 5 | using Serilog; 6 | using Xunit; 7 | 8 | namespace SeqCli.EndToEnd.Sample; 9 | 10 | public class SampleSetupTestCase : ICliTestCase 11 | { 12 | public async Task ExecuteAsync(SeqConnection connection, ILogger logger, CliCommandRunner runner) 13 | { 14 | var exit = runner.Exec("sample setup", "--confirm"); 15 | Assert.Equal(0, exit); 16 | 17 | var sampleWorkspace = (await connection.Workspaces.ListAsync(shared: true)) 18 | .SingleOrDefault(w => w.Title == "Sample"); 19 | 20 | Assert.NotNull(sampleWorkspace); 21 | } 22 | } -------------------------------------------------------------------------------- /test/SeqCli.Tests/PlainText/StaticMessageTemplateReaderTests.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using SeqCli.Ingestion; 3 | using SeqCli.Tests.Support; 4 | using Xunit; 5 | 6 | namespace SeqCli.Tests.PlainText; 7 | 8 | public class StaticMessageTemplateReaderTests 9 | { 10 | [Fact] 11 | public async Task ReaderSubstitutesMessageTemplate() 12 | { 13 | var evt = Some.LogEvent(); 14 | const string mt = "This is a message template"; 15 | var reader = new FixedLogEventReader(new ReadResult(evt, false)); 16 | var wrapper = new StaticMessageTemplateReader(reader, mt); 17 | var result = await wrapper.TryReadAsync(); 18 | Assert.Equal(mt, result.LogEvent.MessageTemplate.Text); 19 | } 20 | } -------------------------------------------------------------------------------- /test/SeqCli.Tests/Cli/NoDuplicateCommands.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Reflection; 3 | using SeqCli.Cli; 4 | using Xunit; 5 | 6 | namespace SeqCli.Tests.Cli; 7 | 8 | public class NoDuplicateCommands 9 | { 10 | [Fact] 11 | public void EnsureNoDuplicateCommands() 12 | { 13 | var anyDuplicates = typeof(Command).GetTypeInfo().Assembly.GetExportedTypes() 14 | .Where(t => t.IsAssignableFrom(typeof(Command))) 15 | .Select(t => new {CommandType = t, Attribute = t.GetCustomAttribute()}) 16 | .GroupBy(t => new {t.Attribute.Name, t.Attribute.SubCommand}) 17 | .Any(t => t.Count() > 1); 18 | 19 | Assert.False(anyDuplicates); 20 | } 21 | } -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/SeqCli.EndToEnd.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net9.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | PreserveNewest 13 | 14 | 15 | PreserveNewest 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Ingest/SimpleTextIngestionTestCase.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Threading.Tasks; 3 | using Seq.Api; 4 | using SeqCli.EndToEnd.Support; 5 | using Serilog; 6 | using Xunit; 7 | 8 | namespace SeqCli.EndToEnd.Ingest; 9 | 10 | public class SimpleTextIngestionTestCase : ICliTestCase 11 | { 12 | public async Task ExecuteAsync( 13 | SeqConnection connection, 14 | ILogger logger, 15 | CliCommandRunner runner) 16 | { 17 | var inputFiles = Path.Combine("Data", "log-*.txt"); 18 | 19 | var exit = runner.Exec("ingest", $"-i {inputFiles}"); 20 | Assert.Equal(0, exit); 21 | 22 | var events = await connection.Events.ListAsync(); 23 | Assert.Equal(4, events.Count); 24 | } 25 | } -------------------------------------------------------------------------------- /src/SeqCli/Templates/Ast/JsonTemplate.cs: -------------------------------------------------------------------------------- 1 | // Copyright © Datalust Pty Ltd and Contributors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | namespace SeqCli.Templates.Ast; 16 | 17 | abstract class JsonTemplate 18 | { 19 | } -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/User/UserListTestCase.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Seq.Api; 3 | using SeqCli.EndToEnd.Support; 4 | using Serilog; 5 | using Xunit; 6 | 7 | namespace SeqCli.EndToEnd.User; 8 | 9 | public class UserListTestCase : ICliTestCase 10 | { 11 | public Task ExecuteAsync( 12 | SeqConnection connection, 13 | ILogger logger, 14 | CliCommandRunner runner) 15 | { 16 | var exit = runner.Exec("user list"); 17 | Assert.Equal(0, exit); 18 | 19 | exit = runner.Exec("user list", "-n admin"); 20 | Assert.Equal(0, exit); 21 | 22 | var output = runner.LastRunProcess.Output; 23 | Assert.Equal("user-admin admin", output.Trim()); 24 | 25 | return Task.CompletedTask; 26 | } 27 | } -------------------------------------------------------------------------------- /src/SeqCli/Ingestion/InvalidDataHandling.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Datalust Pty Ltd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | namespace SeqCli.Ingestion; 16 | 17 | enum InvalidDataHandling 18 | { 19 | Fail, 20 | Ignore 21 | } -------------------------------------------------------------------------------- /src/SeqCli/Templates/Ast/JsonTemplateNull.cs: -------------------------------------------------------------------------------- 1 | // Copyright © Datalust Pty Ltd and Contributors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | namespace SeqCli.Templates.Ast; 16 | 17 | class JsonTemplateNull : JsonTemplate 18 | { 19 | } -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Autofac; 3 | using SeqCli.EndToEnd; 4 | using SeqCli.EndToEnd.Support; 5 | using Serilog; 6 | using Serilog.Debugging; 7 | 8 | Log.Logger = new LoggerConfiguration() 9 | .WriteTo.Console() 10 | .CreateLogger(); 11 | 12 | SelfLog.Enable(Console.Error); 13 | 14 | try 15 | { 16 | var testDriverArgs = new Args(args); 17 | 18 | var builder = new ContainerBuilder(); 19 | builder.RegisterModule(new TestDriverModule(testDriverArgs)); 20 | await using var container = builder.Build(); 21 | return await container.Resolve().Run(); 22 | } 23 | catch (Exception ex) 24 | { 25 | Log.Fatal(ex, "Testing failed"); 26 | return 1; 27 | } 28 | finally 29 | { 30 | await Log.CloseAndFlushAsync(); 31 | } 32 | -------------------------------------------------------------------------------- /src/SeqCli/PlainText/Parsers/SpanEx.cs: -------------------------------------------------------------------------------- 1 | using Superpower; 2 | using Superpower.Model; 3 | using Superpower.Parsers; 4 | 5 | namespace SeqCli.PlainText.Parsers; 6 | 7 | static class SpanEx 8 | { 9 | public static TextParser MatchedBy(TextParser parser) 10 | { 11 | return i => 12 | { 13 | var result = parser(i); 14 | 15 | if (!result.HasValue) 16 | return Result.CastEmpty(result); 17 | 18 | return Result.Value( 19 | i.Until(result.Remainder), 20 | i, 21 | result.Remainder); 22 | }; 23 | } 24 | 25 | public static TextParser NonWhiteSpace { get; } = 26 | Span.WithoutAny(char.IsWhiteSpace); 27 | } -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/ApiKey/ApiKeyCreateTestCase.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Seq.Api; 3 | using SeqCli.EndToEnd.Support; 4 | using Serilog; 5 | using Xunit; 6 | 7 | namespace SeqCli.EndToEnd.ApiKey; 8 | 9 | public class ApiKeyCreateTestCase : ICliTestCase 10 | { 11 | public Task ExecuteAsync(SeqConnection connection, ILogger logger, CliCommandRunner runner) 12 | { 13 | var exit = runner.Exec("apikey create", "-t Test"); 14 | Assert.Equal(0, exit); 15 | 16 | exit = runner.Exec("apikey list", "-t Test --json --no-color"); 17 | Assert.Equal(0, exit); 18 | 19 | var output = runner.LastRunProcess!.Output; 20 | Assert.Contains("\"AssignedPermissions\": [\"Ingest\"]", output); 21 | 22 | return Task.CompletedTask; 23 | } 24 | } -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Node/NodeHealthTestCase.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Seq.Api; 3 | using SeqCli.EndToEnd.Support; 4 | using Serilog; 5 | using Xunit; 6 | 7 | namespace SeqCli.EndToEnd.Node; 8 | 9 | [CliTestCase] 10 | public class NodeHealthTestCase: ICliTestCase 11 | { 12 | public Task ExecuteAsync(SeqConnection connection, ILogger logger, CliCommandRunner runner) 13 | { 14 | var exit = runner.Exec("node health"); 15 | Assert.Equal(0, exit); 16 | Assert.Equal("200", runner.LastRunProcess!.Output.Trim()); 17 | 18 | exit = runner.Exec("node health --no-color --json"); 19 | Assert.Equal(0, exit); 20 | Assert.StartsWith("{\"status\":", runner.LastRunProcess!.Output); 21 | 22 | return Task.CompletedTask; 23 | } 24 | } -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Ingest/OverrideLevelIngestionTestCase.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using Seq.Api; 5 | using SeqCli.EndToEnd.Support; 6 | using Serilog; 7 | using Xunit; 8 | 9 | namespace SeqCli.EndToEnd.Ingest; 10 | 11 | public class OverrideLevelIngestionTestCase : ICliTestCase 12 | { 13 | public async Task ExecuteAsync( 14 | SeqConnection connection, 15 | ILogger logger, 16 | CliCommandRunner runner) 17 | { 18 | var inputFiles = Path.Combine("Data", "log-*.txt"); 19 | 20 | var exit = runner.Exec("ingest", $"-i {inputFiles} -l OK"); 21 | Assert.Equal(0, exit); 22 | 23 | var events = await connection.Events.ListAsync(); 24 | Assert.Equal(4, events.Count(e => e.Level == "OK")); 25 | } 26 | } -------------------------------------------------------------------------------- /src/SeqCli/Cli/ICommandMetadata.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Datalust Pty Ltd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | namespace SeqCli.Cli; 16 | 17 | interface ICommandMetadata 18 | { 19 | string Name { get; } 20 | string? SubCommand { get; } 21 | string HelpText { get; } 22 | } -------------------------------------------------------------------------------- /src/SeqCli/Templates/Export/EntityName.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Seq.Api.Model; 3 | 4 | namespace SeqCli.Templates.Export; 5 | 6 | static class EntityName 7 | { 8 | public static string FromEntityType(Type entityType) 9 | { 10 | if (!typeof(Entity).IsAssignableFrom(entityType)) 11 | throw new ArgumentException("Type is not an entity type."); 12 | 13 | return entityType.Name.ToLowerInvariant().Replace("entity", ""); 14 | } 15 | 16 | public static string ToResourceGroup(string resource) 17 | { 18 | if (resource.EndsWith('y')) 19 | { 20 | return resource.TrimEnd('y') + "ies"; 21 | } 22 | 23 | if (resource.EndsWith('x')) 24 | { 25 | return resource + "es"; 26 | } 27 | 28 | return resource + "s"; 29 | } 30 | } -------------------------------------------------------------------------------- /test/SeqCli.Tests/Forwarder/Filesystem/InMemoryStoreFileReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using SeqCli.Forwarder.Filesystem; 3 | 4 | namespace SeqCli.Tests.Forwarder.Filesystem; 5 | 6 | class InMemoryStoreFileReader : StoreFileReader 7 | { 8 | readonly int _length; 9 | 10 | readonly InMemoryStoreFile _storeFile; 11 | 12 | public InMemoryStoreFileReader(InMemoryStoreFile storeFile, int length) 13 | { 14 | _storeFile = storeFile; 15 | _length = length; 16 | } 17 | 18 | public override long CopyTo(Span buffer, long from = 0, long? length = null) 19 | { 20 | var span = _storeFile.Contents.AsSpan().Slice((int)from, (int?)length ?? _length); 21 | span.CopyTo(buffer); 22 | 23 | return span.Length; 24 | } 25 | 26 | public override void Dispose() 27 | { 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Ingest/NDJsonIngestionTestCase.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Threading.Tasks; 3 | using Seq.Api; 4 | using SeqCli.EndToEnd.Support; 5 | using Serilog; 6 | using Xunit; 7 | 8 | namespace SeqCli.EndToEnd.Ingest; 9 | 10 | // ReSharper disable once InconsistentNaming 11 | public class NDJsonIngestionTestCase : ICliTestCase 12 | { 13 | public async Task ExecuteAsync( 14 | SeqConnection connection, 15 | ILogger logger, 16 | CliCommandRunner runner) 17 | { 18 | var inputFile = Path.Combine("Data", "events.ndjson"); 19 | Assert.True(File.Exists(inputFile)); 20 | 21 | var exit = runner.Exec("ingest", $"--json -i {inputFile}"); 22 | Assert.Equal(0, exit); 23 | 24 | var events = await connection.Events.ListAsync(); 25 | Assert.Equal(15, events.Count); 26 | } 27 | } -------------------------------------------------------------------------------- /src/SeqCli/Cli/Features/ConfirmFeature.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SeqCli.Cli.Features; 4 | 5 | class ConfirmFeature: CommandFeature 6 | { 7 | bool Yes { get; set; } 8 | 9 | public override void Enable(OptionSet options) 10 | { 11 | options.Add("y|confirm", 12 | "Answer [y]es when prompted to continue", 13 | _ => Yes = true); 14 | } 15 | 16 | public bool TryConfirm(string prompt) 17 | { 18 | if (Yes) 19 | { 20 | return true; 21 | } 22 | 23 | Console.Error.WriteLine($"{prompt} Continue?"); 24 | Console.Error.Write("[y/N]: "); 25 | var k = Console.ReadKey(); 26 | Console.Error.WriteLine(); 27 | return k.Key == ConsoleKey.Y && (k.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) == 0; 28 | } 29 | } -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/License/LicenceShowTestCase.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Seq.Api; 3 | using SeqCli.EndToEnd.Support; 4 | using Serilog; 5 | using Xunit; 6 | 7 | namespace SeqCli.EndToEnd.License; 8 | 9 | public class LicenseShowTestCase : ICliTestCase 10 | { 11 | public async Task ExecuteAsync(SeqConnection connection, ILogger logger, CliCommandRunner runner) 12 | { 13 | var license = await connection.Licenses.FindCurrentAsync(); 14 | Assert.True(license.IsSingleUser); 15 | 16 | runner.Exec("license show"); 17 | Assert.Equal( 18 | "", 19 | runner.LastRunProcess!.Output.Trim()); 20 | 21 | runner.Exec("license show --json"); 22 | Assert.Contains( 23 | "You're using the free Individual license.", 24 | runner.LastRunProcess!.Output.Trim()); 25 | } 26 | } -------------------------------------------------------------------------------- /src/SeqCli/PlainText/Extraction/PatternElement.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics.CodeAnalysis; 3 | using Superpower; 4 | using Superpower.Model; 5 | 6 | namespace SeqCli.PlainText.Extraction; 7 | 8 | abstract class PatternElement 9 | { 10 | readonly string? _name; 11 | 12 | bool IsIgnored => _name == null; 13 | 14 | protected PatternElement(string? name) 15 | { 16 | _name = name; 17 | } 18 | 19 | public abstract TextParser Match { get; } 20 | 21 | public abstract bool TryExtract( 22 | TextSpan input, 23 | Dictionary result, 24 | out TextSpan remainder); 25 | 26 | protected void CollectResult(Dictionary result, object? value) 27 | { 28 | if (!IsIgnored) 29 | result.Add(_name!, value); 30 | } 31 | } -------------------------------------------------------------------------------- /src/SeqCli/Cli/FeatureVisibility.cs: -------------------------------------------------------------------------------- 1 | // Copyright © Datalust Pty Ltd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using System; 16 | 17 | namespace SeqCli.Cli; 18 | 19 | [Flags] 20 | public enum FeatureVisibility 21 | { 22 | None = 0, 23 | Visible = 1, 24 | Preview = 1 << 1, 25 | Hidden = 1 << 2 26 | } 27 | -------------------------------------------------------------------------------- /test/SeqCli.Tests/Apps/ExampleApp.d.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Example App", 3 | "description": "Just for this test!", 4 | "capabilities": [], 5 | "platform": { 6 | "hosted-dotnet": { 7 | "seqAppTypeName": "SeqCli.Tests.Apps.AppDefinitionFormatterTests+ExampleApp" 8 | } 9 | }, 10 | "settings": { 11 | "PetName": { 12 | "displayName": "Pet name" 13 | }, 14 | "Species": { 15 | "helpText": "The species of your pet.", 16 | "isOptional": true, 17 | "inputType": "select", 18 | "allowedValues": [ 19 | { 20 | "value": "Cat" 21 | }, 22 | { 23 | "value": "Dog" 24 | }, 25 | { 26 | "value": "Goldfish" 27 | }, 28 | { 29 | "value": "ExoticSpecies", 30 | "description": "Exotic species" 31 | } 32 | ] 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test/SeqCli.Tests/Apps/AppLoaderTests.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using SeqCli.Apps; 3 | using SeqCli.Apps.Definitions; 4 | using Xunit; 5 | 6 | namespace SeqCli.Tests.Apps; 7 | 8 | public class AppLoaderTests 9 | { 10 | [Fact] 11 | public void FindsAppConfiguration() 12 | { 13 | const string appBinaries = "Apps/FirstOfTypeBinaries"; 14 | Assert.True(Directory.Exists(appBinaries)); 15 | 16 | using var loader = new AppLoader(appBinaries); 17 | Assert.True(loader.TryLoadSeqAppType(null, out var seqAppType)); 18 | 19 | var definition = AppMetadataReader.ReadFromSeqAppType(seqAppType); 20 | 21 | Assert.Equal("First of Type", definition.Name); 22 | 23 | var typeName = definition.Platform["hosted-dotnet"].SeqAppTypeName; 24 | Assert.Equal("Seq.App.FirstOfType.FirstOfTypeDetector", typeName); 25 | } 26 | } -------------------------------------------------------------------------------- /src/SeqCli/Util/PasswordHash.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Security.Cryptography; 3 | 4 | namespace SeqCli.Util; 5 | 6 | static class PasswordHash 7 | { 8 | const int SaltSize = 16, 9 | HashSize = 64, 10 | HashIter = 500_000; 11 | 12 | public static byte[] GenerateSalt() 13 | { 14 | var salt = new byte[SaltSize]; 15 | using var cp = RandomNumberGenerator.Create(); 16 | cp.GetBytes(salt); 17 | return salt; 18 | } 19 | 20 | public static byte[] Calculate(string password, byte[] salt) 21 | { 22 | if (password == null) throw new ArgumentNullException(nameof(password)); 23 | if (salt == null) throw new ArgumentNullException(nameof(salt)); 24 | 25 | using var algorithm = new Rfc2898DeriveBytes(password, salt, HashIter, HashAlgorithmName.SHA512); 26 | return algorithm.GetBytes(HashSize); 27 | } 28 | } -------------------------------------------------------------------------------- /src/SeqCli/Cli/CommandFeature.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Datalust Pty Ltd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using System.Collections.Generic; 16 | 17 | namespace SeqCli.Cli; 18 | 19 | abstract class CommandFeature 20 | { 21 | public abstract void Enable(OptionSet options); 22 | 23 | public virtual IEnumerable GetUsageErrors() => []; 24 | } -------------------------------------------------------------------------------- /src/SeqCli/PlainText/Framing/Frame.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Datalust Pty Ltd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | namespace SeqCli.PlainText.Framing; 16 | 17 | struct Frame 18 | { 19 | public bool HasValue { get; set; } 20 | public bool IsOrphan { get; set; } 21 | public string Value { get; set; } 22 | public bool IsAtEnd { get; set; } 23 | } -------------------------------------------------------------------------------- /src/SeqCli/Cli/Commands/Profile/ListCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using SeqCli.Cli.Features; 5 | using SeqCli.Config; 6 | 7 | namespace SeqCli.Cli.Commands.Profile; 8 | 9 | [Command("profile", "list", "List connection profiles", 10 | Example = "seqcli profile list")] 11 | class ListCommand : Command 12 | { 13 | readonly StoragePathFeature _storagePath; 14 | 15 | public ListCommand() 16 | { 17 | _storagePath = Enable(); 18 | } 19 | 20 | protected override Task Run() 21 | { 22 | var config = RuntimeConfigurationLoader.Load(_storagePath); 23 | 24 | foreach (var profile in config.Profiles.OrderBy(p => p.Key)) 25 | { 26 | Console.WriteLine($"{profile.Key} ({profile.Value.ServerUrl})"); 27 | } 28 | 29 | return Task.FromResult(0); 30 | } 31 | } -------------------------------------------------------------------------------- /src/SeqCli/Templates/Ast/JsonTemplateBoolean.cs: -------------------------------------------------------------------------------- 1 | // Copyright © Datalust Pty Ltd and Contributors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | namespace SeqCli.Templates.Ast; 16 | 17 | class JsonTemplateBoolean : JsonTemplate 18 | { 19 | public bool Value { get; } 20 | 21 | public JsonTemplateBoolean(bool value) 22 | { 23 | Value = value; 24 | } 25 | } -------------------------------------------------------------------------------- /src/SeqCli/Templates/Ast/JsonTemplateNumber.cs: -------------------------------------------------------------------------------- 1 | // Copyright © Datalust Pty Ltd and Contributors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | namespace SeqCli.Templates.Ast; 16 | 17 | class JsonTemplateNumber : JsonTemplate 18 | { 19 | public decimal Value { get; } 20 | 21 | public JsonTemplateNumber(decimal value) 22 | { 23 | Value = value; 24 | } 25 | } -------------------------------------------------------------------------------- /src/SeqCli/Api/ApiConstants.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Datalust Pty Ltd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | namespace SeqCli.Api; 16 | 17 | static class ApiConstants 18 | { 19 | public const string ClefMediaType = "application/vnd.serilog.clef"; 20 | public const string IngestionEndpoint = "api/events/raw"; 21 | public const string ApiKeyHeaderName = "X-Seq-ApiKey"; 22 | } -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Support/LicenseSetup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Seq.Api; 4 | using Serilog; 5 | 6 | namespace SeqCli.EndToEnd.Support; 7 | 8 | public class LicenseSetup(Args args) 9 | { 10 | readonly bool _enabled = args.Multiuser(); 11 | 12 | bool _attempted; 13 | string _certificate; 14 | 15 | public async Task SetupAsync( 16 | SeqConnection connection, 17 | ILogger logger) 18 | { 19 | if (!_enabled) 20 | return; 21 | 22 | if (!_attempted) 23 | { 24 | _attempted = true; 25 | logger.Information("Reading license certificate from STDIN...."); 26 | _certificate = await Console.In.ReadToEndAsync(); 27 | } 28 | 29 | var license = await connection.Licenses.FindCurrentAsync(); 30 | license.LicenseText = _certificate; 31 | await connection.Licenses.UpdateAsync(license); 32 | } 33 | } -------------------------------------------------------------------------------- /src/SeqCli/Output/StripStructureTypeEnricher.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using SeqCli.Util; 3 | using Serilog.Core; 4 | using Serilog.Data; 5 | using Serilog.Events; 6 | 7 | namespace SeqCli.Output; 8 | 9 | public class StripStructureTypeEnricher : LogEventPropertyValueRewriter, ILogEventEnricher 10 | { 11 | public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) 12 | { 13 | foreach (var property in logEvent.Properties) 14 | { 15 | var updated = LogEventPropertyFactory.SafeCreate(property.Key, Visit(null, property.Value)); 16 | logEvent.AddOrUpdateProperty(updated); 17 | } 18 | } 19 | 20 | protected override LogEventPropertyValue VisitStructureValue(object? state, StructureValue structure) 21 | { 22 | return new StructureValue(structure.Properties.Select(p => 23 | LogEventPropertyFactory.SafeCreate(p.Name, Visit(null, p.Value)))); 24 | } 25 | } -------------------------------------------------------------------------------- /src/SeqCli/Config/SeqCliOutputConfig.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Datalust Pty Ltd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // ReSharper disable UnusedAutoPropertyAccessor.Global 16 | // ReSharper disable AutoPropertyCanBeMadeGetOnly.Global 17 | 18 | namespace SeqCli.Config; 19 | 20 | class SeqCliOutputConfig 21 | { 22 | public bool DisableColor { get; set; } 23 | public bool ForceColor { get; set; } 24 | } -------------------------------------------------------------------------------- /src/SeqCli/Templates/Evaluator/JsonTemplateFunction.cs: -------------------------------------------------------------------------------- 1 | // Copyright © Datalust Pty Ltd and Contributors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using System.Diagnostics.CodeAnalysis; 16 | using SeqCli.Templates.Ast; 17 | 18 | namespace SeqCli.Templates.Evaluator; 19 | 20 | delegate bool JsonTemplateFunction(JsonTemplate[] args, [NotNullWhen(true)] out JsonTemplate? result, [NotNullWhen(false)] out string? error); -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Sample/SampleIngestTestCase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using Seq.Api; 5 | using SeqCli.EndToEnd.Support; 6 | using Serilog; 7 | using Xunit; 8 | 9 | namespace SeqCli.EndToEnd.Sample; 10 | 11 | public class SampleIngestTestCase : ICliTestCase 12 | { 13 | public async Task ExecuteAsync(SeqConnection connection, ILogger logger, CliCommandRunner runner) 14 | { 15 | try 16 | { 17 | runner.Exec("sample ingest", "--setup --confirm", timeout: TimeSpan.FromSeconds(3)); 18 | } 19 | catch 20 | { 21 | // Ignored 22 | } 23 | 24 | var events = await connection.Events.ListAsync(); 25 | Assert.NotEmpty(events); 26 | 27 | var sampleWorkspace = (await connection.Workspaces.ListAsync(shared: true)) 28 | .SingleOrDefault(w => w.Title == "Sample"); 29 | 30 | Assert.NotNull(sampleWorkspace); 31 | } 32 | } -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Version/VersionCommandTestCase.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Seq.Api; 3 | using SeqCli.EndToEnd.Support; 4 | using Serilog; 5 | using Xunit; 6 | 7 | #nullable enable 8 | 9 | namespace SeqCli.EndToEnd.Version; 10 | 11 | public class VersionCommandTestCase : ICliTestCase 12 | { 13 | public Task ExecuteAsync(SeqConnection connection, ILogger logger, CliCommandRunner runner) 14 | { 15 | var exit = runner.Exec("version", disconnected: true); 16 | Assert.Equal(0, exit); 17 | 18 | var version = runner.LastRunProcess!.Output; 19 | 20 | Assert.False(string.IsNullOrWhiteSpace(version)); 21 | Assert.NotEqual("1.0.0", version); 22 | Assert.NotEqual("0.0.0", version); 23 | 24 | // Loose version string to make sure the version appears reasonable 25 | Assert.Matches(@"[0-9]*\.[0-9]*\.[0-9]*(\+\S*)?", version); 26 | 27 | return Task.CompletedTask; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/SeqCli/Cli/Commands/User/UpdateCommand.cs: -------------------------------------------------------------------------------- 1 | // Copyright © Datalust Pty Ltd and Contributors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using Seq.Api; 16 | 17 | namespace SeqCli.Cli.Commands.User; 18 | 19 | [Command("user", "update", 20 | "Update an existing user", 21 | Example="seqcli user update --json '{...}'")] 22 | class UpdateCommand(): 23 | Shared.UpdateCommand("user", nameof(SeqConnection.Users)); 24 | -------------------------------------------------------------------------------- /src/SeqCli/Forwarder/Channel/ForwardingChannelEntry.cs: -------------------------------------------------------------------------------- 1 | // Copyright © Datalust Pty Ltd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using System; 16 | using System.Threading.Tasks; 17 | 18 | namespace SeqCli.Forwarder.Channel; 19 | 20 | // Note, Data is backed by a rented array that the receiver should return. 21 | readonly record struct ForwardingChannelEntry(ArraySegment Data, TaskCompletionSource CompletionSource); 22 | -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Dashboard/RenderTestCase.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Threading.Tasks; 3 | using Seq.Api; 4 | using SeqCli.EndToEnd.Support; 5 | using Serilog; 6 | using Xunit; 7 | 8 | namespace SeqCli.EndToEnd.Dashboard; 9 | 10 | public class RenderTestCase : ICliTestCase 11 | { 12 | public Task ExecuteAsync( 13 | SeqConnection connection, 14 | ILogger logger, 15 | CliCommandRunner runner) 16 | { 17 | var exit = runner.Exec("dashboard list"); 18 | Assert.Equal(0, exit); 19 | 20 | var id = runner.LastRunProcess.Output.Split(' ')[0]; 21 | 22 | exit = runner.Exec("dashboard render", $"-i {id} -c \"All Events\" --last 1d --by 1h --no-color"); 23 | Assert.Equal(0, exit); 24 | 25 | var lines = new StringReader(runner.LastRunProcess.Output); 26 | var firstLine = lines.ReadLine(); 27 | Assert.Equal("\"time\",\"count\"", firstLine); 28 | 29 | return Task.CompletedTask; 30 | } 31 | } -------------------------------------------------------------------------------- /src/SeqCli/Apps/Definitions/AppSettingValue.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Datalust Pty Ltd and Contributors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using Newtonsoft.Json; 16 | 17 | namespace SeqCli.Apps.Definitions; 18 | 19 | class AppSettingValue 20 | { 21 | public string? Value { get; set; } 22 | 23 | [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] 24 | public string? Description { get; set; } 25 | } -------------------------------------------------------------------------------- /src/Roastery/Web/Controller.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using Serilog; 3 | 4 | namespace Roastery.Web; 5 | 6 | abstract class Controller 7 | { 8 | protected ILogger Log { get; } 9 | 10 | protected Controller(ILogger logger) 11 | { 12 | Log = logger.ForContext(GetType()); 13 | } 14 | 15 | protected static HttpResponse Json(object? body, HttpStatusCode statusCode = HttpStatusCode.OK) 16 | { 17 | return new(statusCode, body); 18 | } 19 | 20 | protected HttpResponse BadRequest(string? reason = null) 21 | { 22 | Log.Debug("Bad request: {Reason}", reason); 23 | return new HttpResponse(HttpStatusCode.BadRequest, reason); 24 | } 25 | 26 | protected HttpResponse NotFound() 27 | { 28 | return new HttpResponse(HttpStatusCode.NotFound, "The resource was not found on this server."); 29 | } 30 | 31 | protected HttpResponse OK() 32 | { 33 | return new HttpResponse(HttpStatusCode.OK, null); 34 | } 35 | } -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Ingest/SpanIngestionTestCase.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using Seq.Api; 5 | using SeqCli.EndToEnd.Support; 6 | using Serilog; 7 | using Xunit; 8 | 9 | namespace SeqCli.EndToEnd.Ingest; 10 | 11 | public class SpanIngestionTestCase : ICliTestCase 12 | { 13 | public async Task ExecuteAsync( 14 | SeqConnection connection, 15 | ILogger logger, 16 | CliCommandRunner runner) 17 | { 18 | var inputFile = Path.Combine("Data", "spans.clef"); 19 | Assert.True(File.Exists(inputFile)); 20 | 21 | var exit = runner.Exec("ingest", $"--json -i {inputFile}"); 22 | Assert.Equal(0, exit); 23 | 24 | var events = await connection.Events.ListAsync(); 25 | Assert.Equal(3, events.Count); 26 | Assert.Equal(3, events.Count(e => e.TraceId == "af2f559d8ac24a288d2aaef55439778b")); 27 | Assert.Equal(3, events.Count(e => e.SpanId == "5b08fd4ac3aa42d4")); 28 | } 29 | } -------------------------------------------------------------------------------- /test/SeqCli.Tests/Forwarder/Filesystem/InMemoryStoreFileAppender.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using SeqCli.Forwarder.Filesystem; 4 | 5 | namespace SeqCli.Tests.Forwarder.Filesystem; 6 | 7 | class InMemoryStoreFileAppender : StoreFileAppender 8 | { 9 | readonly List _incoming; 10 | 11 | readonly InMemoryStoreFile _storeFile; 12 | 13 | public InMemoryStoreFileAppender(InMemoryStoreFile storeFile) 14 | { 15 | _storeFile = storeFile; 16 | _incoming = new List(); 17 | } 18 | 19 | public override void Append(Span data) 20 | { 21 | _incoming.AddRange(data); 22 | } 23 | 24 | public override long Commit() 25 | { 26 | _storeFile.Append(_incoming.ToArray()); 27 | _incoming.Clear(); 28 | 29 | return _storeFile.Contents.Length; 30 | } 31 | 32 | public override void Sync() 33 | { 34 | } 35 | 36 | public override void Dispose() 37 | { 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/SeqCli/Cli/Commands/Signal/UpdateCommand.cs: -------------------------------------------------------------------------------- 1 | // Copyright © Datalust Pty Ltd and Contributors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using Seq.Api; 16 | 17 | namespace SeqCli.Cli.Commands.Signal; 18 | 19 | [Command("signal", "update", 20 | "Update an existing signal", 21 | Example="seqcli signal update --json '{...}'")] 22 | class UpdateCommand(): 23 | Shared.UpdateCommand("signal", nameof(SeqConnection.Signals)); 24 | -------------------------------------------------------------------------------- /src/SeqCli/Output/OutputFormatter.cs: -------------------------------------------------------------------------------- 1 | using SeqCli.Ingestion; 2 | using SeqCli.Levels; 3 | using Serilog.Formatting; 4 | using Serilog.Templates; 5 | using Serilog.Templates.Themes; 6 | 7 | namespace SeqCli.Output; 8 | 9 | static class OutputFormatter 10 | { 11 | // This is the only usage of Serilog.Expressions remaining in seqcli; the upstream Seq.Syntax doesn't yet support 12 | // the `@sp` property, because it needs to load on older Seq installs with older Serilog versions embedded in the 13 | // app runner. Once we've updated it, we can switch this to a Seq.Syntax template. 14 | internal static ITextFormatter Json(TemplateTheme? theme) => new ExpressionTemplate( 15 | $"{{ {{@t, @mt, @l: coalesce({LevelMapping.SurrogateLevelProperty}, if @l = 'Information' then undefined() else @l), @x, @sp, @tr, @ps: coalesce({TraceConstants.ParentSpanIdProperty}, @ps), @st: coalesce({TraceConstants.SpanStartTimestampProperty}, @st), ..rest()}} }}\n", 16 | theme: theme 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /src/SeqCli/Cli/Commands/Feed/UpdateCommand.cs: -------------------------------------------------------------------------------- 1 | // Copyright © Datalust Pty Ltd and Contributors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using Seq.Api; 16 | 17 | namespace SeqCli.Cli.Commands.Feed; 18 | 19 | [Command("feed", "update", 20 | "Update an existing NuGet feed", 21 | Example="seqcli feed update --json '{...}'")] 22 | class UpdateCommand(): 23 | Shared.UpdateCommand("feed", nameof(SeqConnection.Feeds), "NuGet feed"); 24 | -------------------------------------------------------------------------------- /src/SeqCli/Templates/Ast/JsonTemplateString.cs: -------------------------------------------------------------------------------- 1 | // Copyright © Datalust Pty Ltd and Contributors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using System; 16 | 17 | namespace SeqCli.Templates.Ast; 18 | 19 | class JsonTemplateString : JsonTemplate 20 | { 21 | public string Value { get; } 22 | 23 | public JsonTemplateString(string value) 24 | { 25 | Value = value ?? throw new ArgumentNullException(nameof(value)); 26 | } 27 | } -------------------------------------------------------------------------------- /src/SeqCli/Templates/Import/GenericEntity.cs: -------------------------------------------------------------------------------- 1 | // Copyright © Datalust Pty Ltd and Contributors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using Seq.Api.Model; 16 | 17 | namespace SeqCli.Templates.Import; 18 | 19 | // ReSharper disable once ClassNeverInstantiated.Global 20 | class GenericEntity : Entity 21 | { 22 | public string? Title { get; set; } 23 | public string? Name { get; set; } 24 | public string? Expression { get; set; } 25 | } -------------------------------------------------------------------------------- /src/SeqCli/Cli/Commands/ApiKey/UpdateCommand.cs: -------------------------------------------------------------------------------- 1 | // Copyright © Datalust Pty Ltd and Contributors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using Seq.Api; 16 | 17 | namespace SeqCli.Cli.Commands.ApiKey; 18 | 19 | [Command("apikey", "update", 20 | "Update an existing API key", 21 | Example="seqcli apikey update --json '{...}'")] 22 | class UpdateCommand(): 23 | Shared.UpdateCommand("apikey", nameof(SeqConnection.ApiKeys), "API key"); 24 | -------------------------------------------------------------------------------- /src/SeqCli/Forwarder/Storage/BufferReaderChunkExtents.cs: -------------------------------------------------------------------------------- 1 | // Copyright © Datalust Pty Ltd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | namespace SeqCli.Forwarder.Storage; 16 | 17 | /// 18 | /// The current read and write positions in a . 19 | /// 20 | readonly record struct BufferReaderChunkExtents(long CommitHead, long WriteHead) 21 | { 22 | public long Unadvanced => WriteHead - CommitHead; 23 | } 24 | -------------------------------------------------------------------------------- /src/SeqCli/Cli/Commands/Workspace/UpdateCommand.cs: -------------------------------------------------------------------------------- 1 | // Copyright © Datalust Pty Ltd and Contributors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using Seq.Api; 16 | 17 | namespace SeqCli.Cli.Commands.Workspace; 18 | 19 | [Command("workspace", "update", 20 | "Update an existing workspace", 21 | Example="seqcli workspace update --json '{...}'")] 22 | class UpdateCommand(): 23 | Shared.UpdateCommand("workspace", nameof(SeqConnection.Workspaces)); 24 | -------------------------------------------------------------------------------- /src/SeqCli/Output/RedundantEventTypeRemovalEnricher.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Datalust Pty Ltd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using Serilog.Core; 16 | using Serilog.Events; 17 | 18 | namespace SeqCli.Output; 19 | 20 | public class RedundantEventTypeRemovalEnricher : ILogEventEnricher 21 | { 22 | public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) 23 | { 24 | logEvent.RemovePropertyIfPresent("@i"); 25 | } 26 | } -------------------------------------------------------------------------------- /src/SeqCli/Forwarder/Filesystem/EmptyStoreFileReader.cs: -------------------------------------------------------------------------------- 1 | // Copyright © Datalust Pty Ltd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using System; 16 | 17 | namespace SeqCli.Forwarder.Filesystem; 18 | 19 | sealed class EmptyStoreFileReader : StoreFileReader 20 | { 21 | public override void Dispose() 22 | { 23 | } 24 | 25 | public override long CopyTo(Span buffer, long from = 0, long? length = null) 26 | { 27 | return 0; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/SeqCli/Templates/Ast/JsonTemplateArray.cs: -------------------------------------------------------------------------------- 1 | // Copyright © Datalust Pty Ltd and Contributors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using System; 16 | 17 | namespace SeqCli.Templates.Ast; 18 | 19 | class JsonTemplateArray : JsonTemplate 20 | { 21 | public JsonTemplate[] Elements { get; } 22 | 23 | public JsonTemplateArray(JsonTemplate[] elements) 24 | { 25 | Elements = elements ?? throw new ArgumentNullException(nameof(elements)); 26 | } 27 | } -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Signal/SignalCreateTestCase.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Threading.Tasks; 3 | using Seq.Api; 4 | using SeqCli.EndToEnd.Support; 5 | using Serilog; 6 | using Xunit; 7 | 8 | namespace SeqCli.EndToEnd.Signal; 9 | 10 | public class SignalCreateTestCase : ICliTestCase 11 | { 12 | public async Task ExecuteAsync( 13 | SeqConnection connection, 14 | ILogger logger, 15 | CliCommandRunner runner) 16 | { 17 | var exit = runner.Exec("signal create", "-t TestSignal -f \"@Exception is not null\" -c Column1 -c \"round(Property2, 1)\""); 18 | Assert.Equal(0, exit); 19 | 20 | var signals = await connection.Signals.ListAsync(shared: true); 21 | var testSignal = signals.First(x => x.Title == "TestSignal"); 22 | 23 | Assert.Equal("@Exception is not null", testSignal.Filters[0].Filter); 24 | Assert.Equal("Column1", testSignal.Columns[0].Expression); 25 | Assert.Equal("round(Property2, 1)", testSignal.Columns[1].Expression); 26 | } 27 | } -------------------------------------------------------------------------------- /src/Roastery/Agents/WarehouseStaff.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Roastery.Model; 5 | using Roastery.Web; 6 | 7 | namespace Roastery.Agents; 8 | 9 | class WarehouseStaff : Agent 10 | { 11 | readonly HttpClient _client; 12 | 13 | public WarehouseStaff(HttpClient client) 14 | : base(20000) 15 | { 16 | _client = client; 17 | } 18 | 19 | protected override IEnumerable GetBehaviors() 20 | { 21 | yield return ShipOrders; 22 | } 23 | 24 | async Task ShipOrders(CancellationToken cancellationToken) 25 | { 26 | var orders = await _client.GetAsync>("api/orders"); 27 | foreach (var order in orders) 28 | { 29 | if (order.Status == OrderStatus.PendingShipment) 30 | { 31 | order.Status = OrderStatus.Shipped; 32 | await _client.PutAsync($"api/orders/{order.Id}", order); 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/License/LicenseApplyTestsCasecs.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Threading.Tasks; 3 | using Seq.Api; 4 | using SeqCli.EndToEnd.Support; 5 | using Serilog; 6 | using Xunit; 7 | 8 | namespace SeqCli.EndToEnd.License; 9 | 10 | public class LicenseApplyTestCase : ICliTestCase 11 | { 12 | readonly TestDataFolder _testDataFolder; 13 | 14 | public LicenseApplyTestCase(TestDataFolder testDataFolder) 15 | { 16 | _testDataFolder = testDataFolder; 17 | } 18 | 19 | public Task ExecuteAsync(SeqConnection connection, ILogger logger, CliCommandRunner runner) 20 | { 21 | var filename = _testDataFolder.ItemPath("license.txt"); 22 | File.WriteAllText(filename, "Ceci n'est pas une licence"); 23 | runner.Exec("license apply", $"--certificate=\"{filename}\""); 24 | Assert.Contains( 25 | "The license format is invalid: data precedes any keys.", 26 | runner.LastRunProcess!.Output.Trim()); 27 | return Task.CompletedTask; 28 | } 29 | } -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/App/AppBasicsTestCase.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Seq.Api; 3 | using SeqCli.EndToEnd.Support; 4 | using Serilog; 5 | using Xunit; 6 | 7 | namespace SeqCli.EndToEnd.App; 8 | 9 | // ReSharper disable once UnusedType.Global 10 | public class AppBasicsTestCase: ICliTestCase 11 | { 12 | public Task ExecuteAsync(SeqConnection connection, ILogger logger, CliCommandRunner runner) 13 | { 14 | var exit = runner.Exec("feed list", "-n nuget.org"); 15 | Assert.Equal(0, exit); 16 | 17 | exit = runner.Exec("app install", "--package-id Seq.App.EmailPlus"); 18 | Assert.Equal(0, exit); 19 | 20 | exit = runner.Exec("app list", "--package-id Seq.App.EmailPlus"); 21 | Assert.Equal(0, exit); 22 | 23 | exit = runner.Exec("app update", "--all"); 24 | Assert.Equal(0, exit); 25 | 26 | exit = runner.Exec("app uninstall", "--package-id Seq.App.EmailPlus"); 27 | Assert.Equal(0, exit); 28 | 29 | return Task.CompletedTask; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/SeqCli/Signals/SignalExpressionToken.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Datalust Pty Ltd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using Superpower.Display; 16 | 17 | namespace SeqCli.Signals; 18 | 19 | enum SignalExpressionToken 20 | { 21 | Id, 22 | [Token(Category = "operator", Example = ",")] 23 | Comma, 24 | [Token(Category = "operator", Example = "~")] 25 | Tilde, 26 | [Token(Example = "(")] 27 | LParen, 28 | [Token(Example = ")")] 29 | RParen 30 | } 31 | -------------------------------------------------------------------------------- /test/SeqCli.Tests/Forwarder/Storage/IdentifierTests.cs: -------------------------------------------------------------------------------- 1 | using SeqCli.Forwarder.Storage; 2 | using Xunit; 3 | 4 | namespace SeqCli.Tests.Forwarder.Storage; 5 | 6 | public class IdentifierTests 7 | { 8 | [Theory] 9 | [InlineData("0000000000000000.clef", 0)] 10 | [InlineData("0000000000000001.clef", 1)] 11 | [InlineData("000000000000000a.clef", 10)] 12 | [InlineData("ffffffffffffffff.clef", ulong.MaxValue)] 13 | public void ParseValid(string name, ulong expected) 14 | { 15 | Assert.True(ChunkName.TryParse(name, out var actual)); 16 | 17 | Assert.Equal(expected, actual.Value.Id); 18 | Assert.Equal(name, actual.Value.ToString()); 19 | } 20 | 21 | [Theory] 22 | [InlineData("0.clef")] 23 | [InlineData("one.clef")] 24 | [InlineData("00000000000.clef.value")] 25 | [InlineData("0ffffffffffffffff.clef")] 26 | [InlineData("0xffffffffffffff.clef")] 27 | public void ParseInvalid(string name) 28 | { 29 | Assert.False(ChunkName.TryParse(name, out _)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/SeqCli/Cli/Commands/AppInstance/UpdateCommand.cs: -------------------------------------------------------------------------------- 1 | // Copyright © Datalust Pty Ltd and Contributors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using Seq.Api; 16 | 17 | namespace SeqCli.Cli.Commands.AppInstance; 18 | 19 | [Command("appinstance", "update", 20 | "Update an existing app instance", 21 | Example="seqcli appinstance update --json '{...}'")] 22 | class UpdateCommand(): 23 | Shared.UpdateCommand("appinstance", nameof(SeqConnection.AppInstances), "app instance"); 24 | -------------------------------------------------------------------------------- /src/SeqCli/Templates/Ast/JsonTemplateObject.cs: -------------------------------------------------------------------------------- 1 | // Copyright © Datalust Pty Ltd and Contributors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using System.Collections.Generic; 16 | 17 | namespace SeqCli.Templates.Ast; 18 | 19 | class JsonTemplateObject : JsonTemplate 20 | { 21 | public IReadOnlyDictionary Members { get; } 22 | 23 | public JsonTemplateObject(IReadOnlyDictionary members) 24 | { 25 | Members = members; 26 | } 27 | } -------------------------------------------------------------------------------- /src/SeqCli/Cli/Commands/Bench/BenchCasesCollection.cs: -------------------------------------------------------------------------------- 1 | // Copyright © Datalust Pty Ltd and Contributors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using System.Collections.Generic; 16 | 17 | namespace SeqCli.Cli.Commands.Bench; 18 | 19 | /* 20 | * A target type for deserialization of bench case files. 21 | */ 22 | class BenchCasesCollection 23 | { 24 | // ReSharper disable once CollectionNeverUpdated.Global 25 | public IList Cases { get; } = new List(); 26 | } 27 | -------------------------------------------------------------------------------- /src/SeqCli/Cli/Commands/RetentionPolicy/UpdateCommand.cs: -------------------------------------------------------------------------------- 1 | // Copyright © Datalust Pty Ltd and Contributors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using Seq.Api; 16 | 17 | namespace SeqCli.Cli.Commands.RetentionPolicy; 18 | 19 | [Command("retention", "update", 20 | "Update an existing retention policy", 21 | Example="seqcli retention update --json '{...}'")] 22 | class UpdateCommand(): 23 | Shared.UpdateCommand("retention", nameof(SeqConnection.RetentionPolicies), "retention policy"); 24 | -------------------------------------------------------------------------------- /test/SeqCli.Tests/Config/EnvironmentOverridesTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using SeqCli.Config; 3 | using Xunit; 4 | 5 | namespace SeqCli.Tests.Config; 6 | 7 | public class EnvironmentOverridesTests 8 | { 9 | [Fact] 10 | public void EnvironmentVariableOverridesAreApplied() 11 | { 12 | const string initialUrl = "https://old.example.com"; 13 | 14 | var config = new SeqCliConfig 15 | { 16 | Connection = 17 | { 18 | ServerUrl = initialUrl 19 | } 20 | }; 21 | 22 | var environment = new Dictionary(); 23 | EnvironmentOverrides.Apply("SEQCLI_", config, environment); 24 | 25 | Assert.Equal(initialUrl, config.Connection.ServerUrl); 26 | 27 | const string updatedUrl = "https://new.example.com"; 28 | environment["SEQCLI_CONNECTION_SERVERURL"] = updatedUrl; 29 | EnvironmentOverrides.Apply("SEQCLI_", config, environment); 30 | 31 | Assert.Equal(updatedUrl, config.Connection.ServerUrl); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/SeqCli/Cli/CommandMetadata.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Datalust Pty Ltd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | namespace SeqCli.Cli; 16 | 17 | public class CommandMetadata : ICommandMetadata 18 | { 19 | public required string Name { get; set; } 20 | public string? SubCommand { get; set; } 21 | public required string HelpText { get; set; } 22 | public string? Example { get; set; } 23 | public FeatureVisibility Visibility { get; set; } 24 | public SupportedPlatforms Platforms { get; set; } 25 | } 26 | -------------------------------------------------------------------------------- /src/SeqCli/Cli/Commands/Diagnostics/IngestionLogCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using SeqCli.Api; 4 | using SeqCli.Cli.Features; 5 | using SeqCli.Config; 6 | 7 | namespace SeqCli.Cli.Commands.Diagnostics; 8 | 9 | [Command("diagnostics", "ingestionlog", "Retrieve the ingestion log", 10 | Example = "seqcli diagnostics ingestionlog")] 11 | class IngestionLogCommand : Command 12 | { 13 | readonly ConnectionFeature _connection; 14 | readonly StoragePathFeature _storagePath; 15 | 16 | public IngestionLogCommand() 17 | { 18 | _connection = Enable(); 19 | _storagePath = Enable(); 20 | } 21 | 22 | protected override async Task Run() 23 | { 24 | var config = RuntimeConfigurationLoader.Load(_storagePath); 25 | var connection = SeqConnectionFactory.Connect(_connection, config); 26 | 27 | var ingestionLog = await connection.Diagnostics.GetIngestionLogAsync(); 28 | Console.WriteLine(ingestionLog); 29 | 30 | return 0; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/SeqCli/Forwarder/Filesystem/StoreFileReader.cs: -------------------------------------------------------------------------------- 1 | // Copyright © Datalust Pty Ltd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using System; 16 | 17 | namespace SeqCli.Forwarder.Filesystem; 18 | 19 | abstract class StoreFileReader : IDisposable 20 | { 21 | public abstract void Dispose(); 22 | 23 | /// 24 | /// Copy the complete contents of the reader to the given buffer. 25 | /// 26 | public abstract long CopyTo(Span buffer, long from = 0, long? length = null); 27 | } 28 | -------------------------------------------------------------------------------- /src/SeqCli/Forwarder/Filesystem/System/Unix/Libc.cs: -------------------------------------------------------------------------------- 1 | // Copyright © Datalust Pty Ltd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using System.Runtime.InteropServices; 16 | 17 | namespace SeqCli.Forwarder.Filesystem.System.Unix; 18 | 19 | static class Libc 20 | { 21 | [DllImport("libc")] 22 | public static extern int open(string path, int flags); 23 | 24 | [DllImport("libc")] 25 | public static extern int close(int fd); 26 | 27 | [DllImport("libc")] 28 | public static extern int fsync(int fd); 29 | } 30 | -------------------------------------------------------------------------------- /src/SeqCli/Util/TextException.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2013-2015 Serilog Contributors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using System; 16 | 17 | namespace SeqCli.Util; 18 | 19 | class TextException : Exception 20 | { 21 | readonly string _text; 22 | 23 | public TextException(string text) 24 | : base("This exception type provides ToString() access to details only.") 25 | { 26 | _text = text; 27 | } 28 | 29 | public override string ToString() 30 | { 31 | return _text; 32 | } 33 | } -------------------------------------------------------------------------------- /src/Roastery/Web/HttpRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net.Http; 4 | 5 | namespace Roastery.Web; 6 | 7 | class HttpRequest 8 | { 9 | public string RequestId { get; } = Guid.NewGuid().ToString("n").Substring(10); 10 | 11 | public HttpMethod Method { get; } 12 | public string RawUrl { get; } 13 | public object? Body { get; } 14 | public string Scheme { get; } 15 | public string Query { get; } 16 | public string Path { get; } 17 | public int Port { get; } 18 | public string Host { get; } 19 | 20 | public IDictionary> Headers { get; } = 21 | new Dictionary>(); 22 | 23 | public HttpRequest(HttpMethod method, string url, object? body = null) 24 | { 25 | Method = method; 26 | RawUrl = url; 27 | Body = body; 28 | var parts = new Uri(url); 29 | Scheme = parts.Scheme; 30 | Host = parts.Host; 31 | Port = parts.Port; 32 | Path = parts.AbsolutePath; 33 | Query = parts.Query; 34 | } 35 | } -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Help/MarkdownHelpTestCase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Seq.Api; 4 | using SeqCli.EndToEnd.Support; 5 | using Serilog; 6 | using Xunit; 7 | 8 | #nullable enable 9 | 10 | namespace SeqCli.EndToEnd.Help; 11 | 12 | public class MarkdownHelpTestCase : ICliTestCase 13 | { 14 | public Task ExecuteAsync(SeqConnection connection, ILogger logger, CliCommandRunner runner) 15 | { 16 | var exit = runner.Exec("help", "--markdown", disconnected: true); 17 | Assert.Equal(0, exit); 18 | 19 | var markdown = runner.LastRunProcess!.Output; 20 | 21 | Assert.StartsWith("## Commands", markdown); 22 | 23 | var indexOfTemplateExport = markdown.IndexOf("### `template export`", StringComparison.Ordinal); 24 | var indexOfTemplateImport = markdown.IndexOf("### `template import`", StringComparison.Ordinal); 25 | Assert.NotEqual(indexOfTemplateExport, indexOfTemplateImport); 26 | Assert.True(indexOfTemplateExport < indexOfTemplateImport); 27 | 28 | return Task.CompletedTask; 29 | } 30 | } -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Support/TestDataFolder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace SeqCli.EndToEnd.Support; 5 | 6 | public sealed class TestDataFolder : IDisposable 7 | { 8 | readonly string _basePath; 9 | 10 | public TestDataFolder() 11 | { 12 | _basePath = System.IO.Path.Combine( 13 | System.IO.Path.GetTempPath(), 14 | "SeqCli.Tests.EndToEnd", 15 | Guid.NewGuid().ToString("n")); 16 | 17 | Directory.CreateDirectory(_basePath); 18 | } 19 | 20 | public string Path => _basePath; 21 | 22 | public string ItemPath(string relativePath) 23 | { 24 | return System.IO.Path.Combine(_basePath, relativePath); 25 | } 26 | 27 | public void Dispose() 28 | { 29 | try 30 | { 31 | Directory.Delete(_basePath, true); 32 | } 33 | catch (Exception) 34 | { 35 | Console.ForegroundColor = ConsoleColor.Yellow; 36 | Console.WriteLine("Failed to delete temporary path `{0}`.", Path); 37 | Console.ResetColor(); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /src/SeqCli/PlainText/Extraction/SimplePatternElement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Superpower; 4 | using Superpower.Model; 5 | 6 | namespace SeqCli.PlainText.Extraction; 7 | 8 | class SimplePatternElement : PatternElement 9 | { 10 | readonly TextParser _parser; 11 | 12 | public override TextParser Match { get; } 13 | 14 | public SimplePatternElement(TextParser parser, string? name = null) 15 | : base(name) 16 | { 17 | _parser = parser ?? throw new ArgumentNullException(nameof(parser)); 18 | Match = _parser.Select(s => Unit.Value); 19 | } 20 | 21 | public override bool TryExtract( 22 | TextSpan input, 23 | Dictionary result, 24 | out TextSpan remainder) 25 | { 26 | var match = _parser(input); 27 | if (!match.HasValue) 28 | { 29 | remainder = input; 30 | return false; 31 | } 32 | 33 | CollectResult(result, match.Value); 34 | remainder = match.Remainder; 35 | 36 | return true; 37 | } 38 | } -------------------------------------------------------------------------------- /src/SeqCli/Cli/Features/WaitUntilHealthyFeature.cs: -------------------------------------------------------------------------------- 1 | // Copyright © Datalust Pty Ltd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | namespace SeqCli.Cli.Features; 16 | 17 | class WaitUntilHealthyFeature(string targetName): CommandFeature 18 | { 19 | public bool ShouldWait { get; private set; } 20 | 21 | public override void Enable(OptionSet options) 22 | { 23 | options.Add("wait-until-healthy", $"Wait until the {targetName} returns a status of healthy", _ => 24 | { 25 | ShouldWait = true; 26 | }); 27 | } 28 | } -------------------------------------------------------------------------------- /test/SeqCli.Tests/Signals/SignalExpressionParserTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using SeqCli.Signals; 3 | using Xunit; 4 | 5 | namespace SeqCli.Tests.Signals; 6 | 7 | public class SignalExpressionParserTests 8 | { 9 | [Theory, MemberData(nameof(_sources))] 10 | public void ParseSuccessfully((string, string) inputs) 11 | { 12 | var (input, expected) = inputs; 13 | 14 | var parsed = SignalExpressionParser.ParseExpression(input).ToString(); 15 | 16 | Assert.Equal(expected, parsed); 17 | } 18 | 19 | public static IEnumerable _sources = new []{ 20 | [("signal-1 ", "signal-1")], 21 | 22 | [("(signal-1)", "signal-1")], 23 | 24 | [("signal-1 ,signal-2", "signal-1,signal-2")], 25 | 26 | [(" signal-1,signal-2~ signal-3", "(signal-1,signal-2)~signal-3")], 27 | 28 | [("signal-1,signal-2,(signal-3~signal-4)", "(signal-1,signal-2),(signal-3~signal-4)")], 29 | 30 | new object[] { ("signal-1~( (signal-2~signal-3) ,signal-4)", "signal-1~((signal-2~signal-3),signal-4)") } 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /src/SeqCli/Ingestion/EnrichingReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using Serilog.Core; 5 | 6 | namespace SeqCli.Ingestion; 7 | 8 | class EnrichingReader : ILogEventReader 9 | { 10 | readonly ILogEventReader _inner; 11 | readonly IReadOnlyCollection _enrichers; 12 | 13 | public EnrichingReader( 14 | ILogEventReader inner, 15 | IReadOnlyCollection enrichers) 16 | { 17 | _inner = inner ?? throw new ArgumentNullException(nameof(inner)); 18 | _enrichers = enrichers ?? throw new ArgumentNullException(nameof(enrichers)); 19 | } 20 | 21 | public async Task TryReadAsync() 22 | { 23 | var result = await _inner.TryReadAsync(); 24 | 25 | if (result.LogEvent != null) 26 | { 27 | foreach (var enricher in _enrichers) 28 | // We're breaking the nullability contract of `ILogEventEnricher.Enrich()`, here. 29 | enricher.Enrich(result.LogEvent, null!); 30 | } 31 | 32 | return result; 33 | } 34 | } -------------------------------------------------------------------------------- /src/SeqCli/SeqCliModule.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Datalust Pty Ltd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using System.Reflection; 16 | using Autofac; 17 | using SeqCli.Cli; 18 | 19 | namespace SeqCli; 20 | 21 | class SeqCliModule : Autofac.Module 22 | { 23 | protected override void Load(ContainerBuilder builder) 24 | { 25 | builder.RegisterType(); 26 | builder.RegisterAssemblyTypes(typeof(Program).GetTypeInfo().Assembly) 27 | .As() 28 | .WithMetadataFrom(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Signal/SignalBasicsTestCase.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Seq.Api; 3 | using SeqCli.EndToEnd.Support; 4 | using Serilog; 5 | using Xunit; 6 | 7 | namespace SeqCli.EndToEnd.Signal; 8 | 9 | public class SignalBasicsTestCase : ICliTestCase 10 | { 11 | public Task ExecuteAsync( 12 | SeqConnection connection, 13 | ILogger logger, 14 | CliCommandRunner runner) 15 | { 16 | var exit = runner.Exec("signal list", "-i signal-none"); 17 | Assert.Equal(1, exit); 18 | 19 | exit = runner.Exec("signal list", "-t Warnings"); 20 | Assert.Equal(0, exit); 21 | 22 | var output = runner.LastRunProcess?.Output; 23 | Assert.Equal("signal-m33302 Warnings", output?.Trim()); 24 | 25 | exit = runner.Exec("signal remove", "-t Warnings"); 26 | Assert.Equal(0, exit); 27 | 28 | exit = runner.Exec("signal list", "-i signal-m33302"); 29 | Assert.Equal(1, exit); 30 | 31 | exit = runner.Exec("signal list", "-t Warnings"); 32 | Assert.Equal(0, exit); 33 | 34 | return Task.CompletedTask; 35 | } 36 | } -------------------------------------------------------------------------------- /src/SeqCli/PlainText/LogEvents/TextOnlyException.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Datalust Pty Ltd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using System; 16 | 17 | namespace SeqCli.PlainText.LogEvents; 18 | 19 | class TextOnlyException : Exception 20 | { 21 | readonly string _toStringValue; 22 | 23 | public TextOnlyException(string toStringValue) 24 | { 25 | _toStringValue = toStringValue ?? throw new ArgumentNullException(nameof(toStringValue)); 26 | } 27 | 28 | public override string ToString() 29 | { 30 | return _toStringValue; 31 | } 32 | } -------------------------------------------------------------------------------- /src/SeqCli/Templates/Ast/JsonTemplateCall.cs: -------------------------------------------------------------------------------- 1 | // Copyright © Datalust Pty Ltd and Contributors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using System; 16 | 17 | namespace SeqCli.Templates.Ast; 18 | 19 | class JsonTemplateCall : JsonTemplate 20 | { 21 | public string Name { get; } 22 | public JsonTemplate[] Arguments { get; } 23 | 24 | public JsonTemplateCall(string name, JsonTemplate[] arguments) 25 | { 26 | Name = name ?? throw new ArgumentNullException(nameof(name)); 27 | Arguments = arguments ?? throw new ArgumentNullException(nameof(arguments)); 28 | } 29 | } -------------------------------------------------------------------------------- /src/SeqCli/Forwarder/Storage/BufferAppenderChunk.cs: -------------------------------------------------------------------------------- 1 | // Copyright © Datalust Pty Ltd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using System; 16 | using SeqCli.Forwarder.Filesystem; 17 | 18 | namespace SeqCli.Forwarder.Storage; 19 | 20 | class BufferAppenderChunk : IDisposable 21 | { 22 | public BufferAppenderChunk(StoreFileAppender appender) 23 | { 24 | Appender = appender; 25 | } 26 | 27 | public StoreFileAppender Appender { get; } 28 | public long WriteHead { get; set; } 29 | 30 | public void Dispose() 31 | { 32 | Appender.Dispose(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/ApiKey/ApiKeyDelegatePermissionsTestCase.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Seq.Api; 3 | using SeqCli.EndToEnd.Support; 4 | using Serilog; 5 | using Xunit; 6 | 7 | namespace SeqCli.EndToEnd.ApiKey; 8 | 9 | [CliTestCase(Multiuser = true)] 10 | public class ApiKeyDelegatePermissionsTestCase : ICliTestCase 11 | { 12 | public Task ExecuteAsync(SeqConnection connection, ILogger logger, CliCommandRunner runner) 13 | { 14 | var exit = runner.Exec( 15 | "user create", 16 | "-n carol -r \"Administrator\" -p test@1234 --no-password-change"); 17 | Assert.Equal(0, exit); 18 | 19 | exit = runner.Exec( 20 | "apikey create", 21 | "-t Setup --permissions=Setup,Write --connect-username=carol --connect-password=\"test@1234\""); 22 | Assert.Equal(0, exit); 23 | 24 | exit = runner.Exec("apikey list", "-t Setup --json --no-color"); 25 | Assert.Equal(0, exit); 26 | 27 | var output = runner.LastRunProcess.Output; 28 | Assert.Contains("\"AssignedPermissions\": [\"Setup\", \"Write\"]", output); 29 | 30 | return Task.CompletedTask; 31 | } 32 | } -------------------------------------------------------------------------------- /src/SeqCli/Encryptor/WindowsNativeDataProtector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Security.Cryptography; 4 | using SeqCli.Util; 5 | 6 | namespace SeqCli.Encryptor; 7 | 8 | class WindowsNativeDataProtector : IDataProtector 9 | { 10 | public byte[] Encrypt(byte[] unencrypted) 11 | { 12 | if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) 13 | throw new PlatformNotSupportedException("Windows native encryption is only supported on Windows"); 14 | 15 | var salt = PasswordHash.GenerateSalt(); 16 | var data = ProtectedData.Protect(unencrypted, salt, DataProtectionScope.LocalMachine); 17 | 18 | return [..data, ..salt]; 19 | } 20 | 21 | public byte[] Decrypt(byte[] encrypted) 22 | { 23 | if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) 24 | throw new PlatformNotSupportedException("Windows native encryption is only supported on Windows"); 25 | 26 | var data = encrypted[..^16]; 27 | var salt = encrypted[^16..]; 28 | 29 | return ProtectedData.Unprotect(data, salt, DataProtectionScope.LocalMachine); 30 | } 31 | } -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Forwarder/ForwarderIngestionLogTestCase.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Seq.Api; 3 | using SeqCli.EndToEnd.Support; 4 | using Serilog; 5 | using Xunit; 6 | 7 | namespace SeqCli.EndToEnd.Forwarder; 8 | 9 | public class ForwarderIngestionLogTestCase: ICliTestCase 10 | { 11 | public async Task ExecuteAsync(SeqConnection connection, ILogger logger, CliCommandRunner runner) 12 | { 13 | var (proc1, listenUri1) = await runner.SpawnForwarderAsync(); 14 | using (proc1) 15 | { 16 | var exit = runner.Exec($"diagnostics ingestionlog -s {listenUri1}", disconnected: true); 17 | Assert.NotEqual(0, exit); 18 | } 19 | 20 | var (proc2, listenUri2) = await runner.SpawnForwarderAsync(environment: new() 21 | { 22 | ["SEQCLI_FORWARDER_DIAGNOSTICS_EXPOSEINGESTIONLOG"] = "True" 23 | }); 24 | using (proc2) 25 | { 26 | var exit = runner.Exec($"diagnostics ingestionlog -s {listenUri2}", disconnected: true); 27 | Assert.Equal(0, exit); 28 | 29 | Assert.StartsWith("[20", runner.LastRunProcess!.Output); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Ingest/StrictClefIngestionTestCase.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using Seq.Api; 5 | using SeqCli.EndToEnd.Support; 6 | using Serilog; 7 | using Xunit; 8 | 9 | namespace SeqCli.EndToEnd.Ingest; 10 | 11 | public class StrictClefIngestionTestCase : ICliTestCase 12 | { 13 | public async Task ExecuteAsync( 14 | SeqConnection connection, 15 | ILogger logger, 16 | CliCommandRunner runner) 17 | { 18 | var inputFile = Path.Combine("Data", "events.clef"); 19 | Assert.True(File.Exists(inputFile)); 20 | 21 | var exit = runner.Exec("ingest", $"--json -i {inputFile}"); 22 | Assert.Equal(0, exit); 23 | 24 | var events = await connection.Events.ListAsync(); 25 | Assert.Equal(15, events.Count); 26 | Assert.Equal(1, events.Count(e => e.Level == "WARN")); 27 | Assert.Equal(2, events.Count(e => e.Level == "Error")); 28 | Assert.Equal(1, events.Count(e => e.Level == "Trace")); 29 | Assert.Equal(1, events.Count(e => e.Level == "Debug")); 30 | Assert.Equal(10, events.Count(e => e.Level == "Information")); 31 | } 32 | } -------------------------------------------------------------------------------- /src/SeqCli/Attribution/Json.NET License.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2007 James Newton-King 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /src/SeqCli/Attribution/Autofac License.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Autofac Project 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/SeqCli/Util/ArgumentString.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Datalust Pty Ltd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using System.Linq; 16 | 17 | namespace SeqCli.Util; 18 | 19 | static class ArgumentString 20 | { 21 | public static string? Normalize(string? argument) 22 | { 23 | return string.IsNullOrWhiteSpace(argument) ? null : argument.Trim(); 24 | } 25 | 26 | public static string[] NormalizeList(string? argument) 27 | { 28 | return (argument ?? "") 29 | .Split(',') 30 | .Select(Normalize) 31 | .Where(s => s != null) 32 | .ToArray()!; 33 | } 34 | } -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Args.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | using System.Linq; 3 | using System.Text.RegularExpressions; 4 | 5 | #nullable enable 6 | 7 | namespace SeqCli.EndToEnd; 8 | 9 | public class Args(params string[] args) 10 | { 11 | public Regex[] TestCases() => args 12 | .Where(arg => !arg.StartsWith("--")) 13 | .Select(ToArgRegex) 14 | .ToArray(); 15 | 16 | // Simple replacement so `Events.*` becomes `Events\..*` 17 | static Regex ToArgRegex(string arg) => new(arg.Replace(".", "\\.").Replace("*", ".*")); 18 | 19 | public bool Multiuser() => args.Any(a => a == "--license-certificate-stdin"); 20 | 21 | public bool UseDockerSeq([NotNullWhen(true)] out string? imageTag, [NotNullWhen(true)] out string? containerRuntime) 22 | { 23 | if (args.Any(a => a == "--docker-server")) 24 | { 25 | imageTag = args.Any(a => a == "--pre") ? "preview" : "latest"; 26 | containerRuntime = args.Any(a => a == "--podman") ? "podman" : "docker"; 27 | return true; 28 | } 29 | 30 | imageTag = null; 31 | containerRuntime = null; 32 | return false; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/SeqCli/Ingestion/BufferingSink.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Threading.Tasks; 4 | using Serilog.Core; 5 | using Serilog.Events; 6 | 7 | namespace SeqCli.Ingestion; 8 | 9 | class BufferingSink: ILogEventSink, ILogEventReader, IDisposable 10 | { 11 | readonly ConcurrentQueue _queue = new(); 12 | const int QueueCapacity = 10000; 13 | volatile bool _disposed; 14 | 15 | public void Emit(LogEvent logEvent) 16 | { // No problem if this is racy - we can afford a bit of extra queue space. 17 | if (_disposed || _queue.Count > QueueCapacity) 18 | return; 19 | 20 | _queue.Enqueue(logEvent); 21 | } 22 | 23 | public Task TryReadAsync() 24 | { 25 | if (!_queue.TryDequeue(out var logEvent)) 26 | return Task.FromResult(new ReadResult(null, _disposed)); 27 | 28 | return Task.FromResult(new ReadResult(logEvent, _disposed)); 29 | } 30 | 31 | public void Dispose() 32 | { 33 | // No problem if this is racy and we end up with leftovers in the queue. 34 | _disposed = true; 35 | _queue.Clear(); 36 | } 37 | } -------------------------------------------------------------------------------- /test/SeqCli.Tests/Templates/JsonTemplateParserTests.cs: -------------------------------------------------------------------------------- 1 | using SeqCli.Templates.Ast; 2 | using SeqCli.Templates.Parser; 3 | using Xunit; 4 | 5 | namespace SeqCli.Tests.Templates; 6 | 7 | public class JsonTemplateParserTests 8 | { 9 | [Fact] 10 | public void TemplatesAreParsed() 11 | { 12 | var template = "{'a':[true, false, null, 12.3], 'b': test(42)}".Replace("'", "\""); 13 | Assert.True(JsonTemplateParser.TryParse(template, out var root, out _, out _)); 14 | 15 | var obj = Assert.IsType(root); 16 | var arr = Assert.IsType(obj.Members["a"]); 17 | var t = Assert.IsType(arr.Elements[0]); 18 | Assert.True(t.Value); 19 | var f = Assert.IsType(arr.Elements[1]); 20 | Assert.False(f.Value); 21 | Assert.IsType(arr.Elements[2]); 22 | var n = Assert.IsType(arr.Elements[3]); 23 | Assert.Equal(12.3m, n.Value); 24 | var call = Assert.IsType(obj.Members["b"]); 25 | Assert.Equal("test", call.Name); 26 | Assert.IsType(call.Arguments[0]); 27 | } 28 | } -------------------------------------------------------------------------------- /src/SeqCli/Cli/Commands/Bench/QueryBenchCase.cs: -------------------------------------------------------------------------------- 1 | // Copyright © Datalust Pty Ltd and Contributors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | namespace SeqCli.Cli.Commands.Bench; 16 | 17 | // ReSharper disable ClassNeverInstantiated.Global AutoPropertyCanBeMadeGetOnly.Global UnusedAutoPropertyAccessor.Global 18 | 19 | class QueryBenchCase 20 | { 21 | public required string Id { get; set; } 22 | public required string Query { get; set; } 23 | public string? SignalExpression { get; set; } 24 | 25 | // Not used programmatically at this time. 26 | // ReSharper disable once UnusedMember.Global 27 | public string? Notes { get; set; } 28 | } -------------------------------------------------------------------------------- /test/SeqCli.Tests/Support/Some.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Security.Cryptography; 4 | using Serilog.Events; 5 | using Serilog.Parsing; 6 | 7 | namespace SeqCli.Tests.Support; 8 | 9 | #nullable enable 10 | 11 | static class Some 12 | { 13 | static readonly RandomNumberGenerator Rng = RandomNumberGenerator.Create(); 14 | 15 | public static LogEvent LogEvent() 16 | { 17 | return new LogEvent( 18 | DateTimeOffset.UtcNow, 19 | LogEventLevel.Information, 20 | null, 21 | new MessageTemplateParser().Parse("Test"), 22 | Enumerable.Empty()); 23 | } 24 | 25 | public static string String() 26 | { 27 | return Guid.NewGuid().ToString("n"); 28 | } 29 | 30 | public static string UriString() 31 | { 32 | return "http://example.com"; 33 | } 34 | 35 | public static byte[] Bytes(int count) 36 | { 37 | var bytes = new byte[count]; 38 | Rng.GetBytes(bytes); 39 | return bytes; 40 | } 41 | 42 | public static string ApiKey() 43 | { 44 | return string.Join("", Bytes(8).Select(v => v.ToString("x2")).ToArray()); 45 | } 46 | } -------------------------------------------------------------------------------- /test/SeqCli.Tests/Config/ConfigFileLocationTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using SeqCli.Cli.Features; 4 | using Xunit; 5 | 6 | namespace SeqCli.Tests.Config; 7 | 8 | public class ConfigFileLocationTests 9 | { 10 | [Fact] 11 | public void DefaultConfigFilename() 12 | { 13 | var storagePathFeature = new StoragePathFeature(_ => null); 14 | 15 | // GetDefaultStorageRoot() isn't exposed by StoragePathFeature because it would introduce the risk we'd 16 | // use it accidentally elsewhere. 17 | var defaultPath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); 18 | 19 | Assert.Equal(defaultPath, storagePathFeature.StorageRootPath); 20 | } 21 | 22 | [Fact] 23 | public void EnvironmentOverridenConfigFilename() 24 | { 25 | var customStoragePath = Path.GetTempPath(); 26 | 27 | var storagePathFeature = new StoragePathFeature( 28 | name => "SEQCLI_STORAGE_PATH".Equals(name, StringComparison.OrdinalIgnoreCase) 29 | ? customStoragePath 30 | : null); 31 | 32 | Assert.Equal(customStoragePath, storagePathFeature.StorageRootPath); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Roastery/Web/SchedulingLatencyMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Roastery.Util; 5 | 6 | namespace Roastery.Web; 7 | 8 | class SchedulingLatencyMiddleware : HttpServer 9 | { 10 | readonly HttpServer _next; 11 | 12 | const int Capacity = 16; // 16 concurrent requests is just fine :-) 13 | int _activeRequests; 14 | 15 | public SchedulingLatencyMiddleware(HttpServer next) 16 | { 17 | _next = next; 18 | } 19 | 20 | public override async Task InvokeAsync(HttpRequest request) 21 | { 22 | var current = Interlocked.Increment(ref _activeRequests); 23 | try 24 | { 25 | var delay = (int)(10 * Distribution.Uniform()); 26 | if (current > Capacity) 27 | { 28 | // One extra millisecond per concurrent request over capacity, ramping up 29 | delay += (int) Math.Pow(current - Capacity, 1.6); 30 | } 31 | await Task.Delay(delay); 32 | return await _next.InvokeAsync(request); 33 | } 34 | finally 35 | { 36 | Interlocked.Decrement(ref _activeRequests); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/SeqCli/Attribution/.NET Core License.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) .NET Foundation and Contributors 4 | 5 | All rights reserved. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Ingest/SerilogTextIngestionTestCase.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using Seq.Api; 5 | using SeqCli.EndToEnd.Support; 6 | using Serilog; 7 | using Xunit; 8 | 9 | namespace SeqCli.EndToEnd.Ingest; 10 | 11 | public class SerilogTextIngestionTestCase : ICliTestCase 12 | { 13 | public async Task ExecuteAsync( 14 | SeqConnection connection, 15 | ILogger logger, 16 | CliCommandRunner runner) 17 | { 18 | var inputFile = Path.Combine("Data", "serilog-events.txt"); 19 | Assert.True(File.Exists(inputFile)); 20 | 21 | var exit = runner.Exec("ingest", $"-i \"{inputFile}\"" + " -x \"{@t:timestamp} [{@l:level}] {@m:*}{:n}{@x:*}\""); 22 | Assert.Equal(0, exit); 23 | 24 | var events = await connection.Events.ListAsync(); 25 | Assert.Equal(3, events.Count); 26 | // Since @l is a level, mapping to full/non-abbreviated names is performed 27 | Assert.Equal(1, events.Count(e => e.Level == "Error")); 28 | Assert.Equal(2, events.Count(e => e.Level == "Information")); 29 | Assert.Equal(1, events.Count(e => e.Exception != null && e.Exception.Contains("MyFlakyMethod()"))); 30 | } 31 | } -------------------------------------------------------------------------------- /src/SeqCli/Cli/Features/DateRangeFeature.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Datalust Pty Ltd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using System; 16 | 17 | namespace SeqCli.Cli.Features; 18 | 19 | class DateRangeFeature : CommandFeature 20 | { 21 | string? _start, _end; 22 | 23 | public override void Enable(OptionSet options) 24 | { 25 | options.Add("start=", "ISO 8601 date/time to query from", v => _start = v); 26 | options.Add("end=", "ISO 8601 date/time to query to", v => _end = v); 27 | } 28 | 29 | public DateTime? Start => _start != null ? DateTime.Parse(_start) : null; 30 | public DateTime? End => _end != null ? DateTime.Parse(_end) : null; 31 | } -------------------------------------------------------------------------------- /src/SeqCli/Cli/Features/InvalidDataHandlingFeature.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Datalust Pty Ltd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using System; 16 | using SeqCli.Ingestion; 17 | 18 | namespace SeqCli.Cli.Features; 19 | 20 | class InvalidDataHandlingFeature : CommandFeature 21 | { 22 | public InvalidDataHandling InvalidDataHandling { get; private set; } 23 | 24 | public override void Enable(OptionSet options) 25 | { 26 | options.Add("invalid-data=", 27 | "Specify how invalid data is handled: `fail` (default) or `ignore`", 28 | v => InvalidDataHandling = (InvalidDataHandling)Enum.Parse(typeof(InvalidDataHandling), v, ignoreCase: true)); 29 | } 30 | } -------------------------------------------------------------------------------- /src/SeqCli/Cli/Features/SendFailureHandlingFeature.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Datalust Pty Ltd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using System; 16 | using SeqCli.Ingestion; 17 | 18 | namespace SeqCli.Cli.Features; 19 | 20 | class SendFailureHandlingFeature : CommandFeature 21 | { 22 | public SendFailureHandling SendFailureHandling { get; private set; } 23 | 24 | public override void Enable(OptionSet options) 25 | { 26 | options.Add("send-failure=", 27 | "Specify how connection failures are handled: `fail` (default), `retry`, `continue`, or `ignore`", 28 | v => SendFailureHandling = Enum.Parse(v, ignoreCase: true)); 29 | } 30 | } -------------------------------------------------------------------------------- /src/SeqCli/Ingestion/StaticMessageTemplateReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using SeqCli.Util; 5 | using Serilog.Events; 6 | using Serilog.Parsing; 7 | 8 | namespace SeqCli.Ingestion; 9 | 10 | class StaticMessageTemplateReader : ILogEventReader 11 | { 12 | readonly ILogEventReader _inner; 13 | readonly MessageTemplate _messageTemplate; 14 | 15 | public StaticMessageTemplateReader(ILogEventReader inner, string messageTemplate) 16 | { 17 | _inner = inner ?? throw new ArgumentNullException(nameof(inner)); 18 | _messageTemplate = new MessageTemplateParser().Parse(messageTemplate); 19 | } 20 | 21 | public async Task TryReadAsync() 22 | { 23 | var result = await _inner.TryReadAsync(); 24 | 25 | if (result.LogEvent == null) 26 | return result; 27 | 28 | var evt = new LogEvent( 29 | result.LogEvent.Timestamp, 30 | result.LogEvent.Level, 31 | result.LogEvent.Exception, 32 | _messageTemplate, 33 | result.LogEvent.Properties.Select(kv => LogEventPropertyFactory.SafeCreate(kv.Key, kv.Value))); 34 | 35 | return new ReadResult(evt, result.IsAtEnd); 36 | } 37 | } -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Data/events.clef: -------------------------------------------------------------------------------- 1 | {"@t":"2017-04-20T04:24:47.0251719Z","@mt":"Loop {Counter}","Counter":0} 2 | {"@t":"2017-04-20T04:24:47.0371689Z","@mt":"Loop {Counter}","Counter":1} 3 | {"@t":"2017-04-20T04:24:47.0371689Z","@mt":"Loop {Counter}","Counter":2} 4 | {"@t":"2017-04-20T04:24:47.0371689Z","@mt":"Loop {Counter}","Counter":3} 5 | {"@t":"2017-04-20T04:24:47.0371689Z","@mt":"Loop {Counter}","Counter":4,"@l":"Error"} 6 | {"@t":"2017-04-20T04:24:47.0371689Z","@mt":"Loop {Counter}","Counter":5} 7 | {"@t":"2017-04-20T04:24:47.0371689Z","@mt":"Loop {Counter}","Counter":6} 8 | {"@t":"2017-04-20T04:24:47.0371689Z","@mt":"Loop {Counter}","Counter":7,"@l":"Error"} 9 | {"@t":"2017-04-20T04:24:47.0371689Z","@mt":"Loop {Counter}","Counter":8} 10 | {"@t":"2017-04-20T04:24:47.0371689Z","@mt":"Loop {Counter}","Counter":9,"@l":"WARN"} 11 | {"@t":"2017-04-20T04:24:47.0371689Z","@mt":"Loop {Counter}","Counter":10} 12 | {"@t":"2017-04-20T04:24:47.0371689Z","@mt":"Loop {Counter}","Counter":11} 13 | {"@t":"2017-04-20T04:24:47.0371689Z","@mt":"Loop {Counter}","Counter":12,"@l":"Trace"} 14 | {"@t":"2017-04-20T04:24:47.0371689Z","@mt":"Loop {Counter}","Counter":13,"@l":"Information"} 15 | {"@t":"2017-04-20T04:24:47.0371689Z","@mt":"Loop {Counter}","Counter":14,"@l":"Debug"} 16 | -------------------------------------------------------------------------------- /src/SeqCli/Cli/Commands/Settings/NamesCommand.cs: -------------------------------------------------------------------------------- 1 | // Copyright © Datalust Pty Ltd and Contributors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using System; 16 | using System.Linq; 17 | using System.Threading.Tasks; 18 | using Seq.Api.Model.Settings; 19 | 20 | namespace SeqCli.Cli.Commands.Settings; 21 | 22 | [Command("setting", "names", "Print the names of all supported settings")] 23 | class NamesCommand: Command 24 | { 25 | protected override Task Run(string[] unrecognized) 26 | { 27 | foreach (var name in Enum.GetNames(typeof(SettingName)).Order()) 28 | { 29 | Console.WriteLine(name); 30 | } 31 | 32 | return Task.FromResult(0); 33 | } 34 | } -------------------------------------------------------------------------------- /src/SeqCli/Ingestion/ScalarPropertyEnricher.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Datalust Pty Ltd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using SeqCli.Util; 16 | using Serilog.Core; 17 | using Serilog.Events; 18 | 19 | namespace SeqCli.Ingestion; 20 | 21 | class ScalarPropertyEnricher : ILogEventEnricher 22 | { 23 | readonly LogEventProperty _property; 24 | 25 | public ScalarPropertyEnricher(string name, object? scalarValue) 26 | { 27 | _property = LogEventPropertyFactory.SafeCreate(name, new ScalarValue(scalarValue)); 28 | } 29 | 30 | public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) 31 | { 32 | logEvent.AddOrUpdateProperty(_property); 33 | } 34 | } -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/User/UserCreateRemoveTestCase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Seq.Api; 4 | using SeqCli.EndToEnd.Support; 5 | using Serilog; 6 | using Xunit; 7 | using System.IO; 8 | using System.Linq; 9 | 10 | namespace SeqCli.EndToEnd.User; 11 | 12 | [CliTestCase(Multiuser = true)] 13 | public class UserCreateRemoveTestCase : ICliTestCase 14 | { 15 | public async Task ExecuteAsync( 16 | SeqConnection connection, 17 | ILogger logger, 18 | CliCommandRunner runner) 19 | { 20 | var exit = runner.Exec( 21 | "user create", 22 | "-n alice -d \"Alice Example\" -r \"User (read/write)\" -p test@1234"); 23 | Assert.Equal(0, exit); 24 | 25 | var id = runner.LastRunProcess.Output.Trim().Split(' ', StringSplitOptions.RemoveEmptyEntries).FirstOrDefault(); 26 | Assert.NotNull(id); 27 | 28 | var user = await connection.Users.FindAsync(id); 29 | Assert.All(user.RoleIds, r => r.StartsWith("role-", StringComparison.OrdinalIgnoreCase)); 30 | 31 | exit = runner.Exec("user remove", $"-i {id}"); 32 | Assert.Equal(0, exit); 33 | 34 | exit = runner.Exec("user list", "-i {id}"); 35 | Assert.Equal(1, exit); 36 | } 37 | } -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Ingest/LevelledTextIngestionTestCase.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using Seq.Api; 5 | using SeqCli.EndToEnd.Support; 6 | using Serilog; 7 | using Xunit; 8 | 9 | namespace SeqCli.EndToEnd.Ingest; 10 | 11 | public class LevelledTextIngestionTestCase : ICliTestCase 12 | { 13 | public async Task ExecuteAsync( 14 | SeqConnection connection, 15 | ILogger logger, 16 | CliCommandRunner runner) 17 | { 18 | var inputFile = Path.Combine("Data", "levelled-events.txt"); 19 | Assert.True(File.Exists(inputFile)); 20 | 21 | var exit = runner.Exec("ingest", $"-i \"{inputFile}\"" + " -x \"{@l:token} {@m:line}\""); 22 | Assert.Equal(0, exit); 23 | 24 | var events = await connection.Events.ListAsync(render: true); 25 | Assert.Equal(2, events.Count); 26 | Assert.Equal(1, events.Count(e => e.RenderedMessage == "Hello")); 27 | // Since @l is a token, no level mapping is applied 28 | Assert.Equal(1, events.Count(e => e.Level == "INFO")); 29 | Assert.Equal(1, events.Count(e => e.Level == "WARN")); 30 | 31 | Assert.DoesNotContain(events, e => e.Properties.Any(p => p.Name.StartsWith("_Seqcli"))); 32 | } 33 | } -------------------------------------------------------------------------------- /src/Roastery/Util/Distribution.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.CompilerServices; 4 | using System.Threading; 5 | 6 | namespace Roastery.Util; 7 | 8 | static class Distribution 9 | { 10 | static readonly Random Rng = new(DateTime.Now.Millisecond); 11 | 12 | [MethodImpl(MethodImplOptions.Synchronized)] 13 | public static double Uniform(double min, double max) 14 | { 15 | if (min < 0) 16 | throw new ArgumentOutOfRangeException(nameof(min)); 17 | 18 | if (max < min || max - min < double.Epsilon) 19 | throw new ArgumentOutOfRangeException(nameof(max)); 20 | 21 | return min + Rng.NextDouble() * (max - min); 22 | } 23 | 24 | [MethodImpl(MethodImplOptions.Synchronized)] 25 | public static T Uniform(IList items) 26 | { 27 | var i = (int) Uniform(0, items.Count); 28 | return items[i]; 29 | } 30 | 31 | [MethodImpl(MethodImplOptions.Synchronized)] 32 | public static double Uniform() 33 | { 34 | return Rng.NextDouble(); 35 | } 36 | 37 | [MethodImpl(MethodImplOptions.Synchronized)] 38 | public static bool OnceIn(int times) 39 | { 40 | return Uniform(0, times) < 1.0; 41 | } 42 | } -------------------------------------------------------------------------------- /test/SeqCli.Tests/Forwarder/Filesystem/InMemoryStoreFile.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | 3 | using System; 4 | using System.Diagnostics.CodeAnalysis; 5 | using SeqCli.Forwarder.Filesystem; 6 | 7 | namespace SeqCli.Tests.Forwarder.Filesystem; 8 | 9 | class InMemoryStoreFile : StoreFile 10 | { 11 | public byte[] Contents { get; private set; } = Array.Empty(); 12 | 13 | public override bool TryGetLength([NotNullWhen(true)] out long? length) 14 | { 15 | length = Contents.Length; 16 | return true; 17 | } 18 | 19 | public void Append(Span incoming) 20 | { 21 | var newContents = new byte[Contents.Length + incoming.Length]; 22 | 23 | Contents.CopyTo(newContents.AsSpan()); 24 | incoming.CopyTo(newContents.AsSpan()[^incoming.Length..]); 25 | 26 | Contents = newContents; 27 | } 28 | 29 | public override bool TryOpenRead(long length, [NotNullWhen(true)] out StoreFileReader? reader) 30 | { 31 | reader = new InMemoryStoreFileReader(this, (int)length); 32 | return true; 33 | } 34 | 35 | public override bool TryOpenAppend([NotNullWhen(true)] out StoreFileAppender? appender) 36 | { 37 | appender = new InMemoryStoreFileAppender(this); 38 | return true; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/SeqCli/Util/LogEventPropertyFactory.cs: -------------------------------------------------------------------------------- 1 | // Copyright © Datalust Pty Ltd and Contributors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using System; 16 | using Serilog.Events; 17 | 18 | namespace SeqCli.Util; 19 | 20 | static class LogEventPropertyFactory 21 | { 22 | const string InvalidPropertyNameSubstitute = "(unnamed)"; 23 | 24 | public static LogEventProperty SafeCreate(string name, LogEventPropertyValue value) 25 | { 26 | if (value == null) throw new ArgumentNullException(nameof(value)); 27 | 28 | if (!LogEventProperty.IsValidName(name)) 29 | name = InvalidPropertyNameSubstitute; 30 | 31 | return new LogEventProperty(name, value); 32 | } 33 | } -------------------------------------------------------------------------------- /src/SeqCli/Apps/Definitions/AppSettingType.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Datalust Pty Ltd and Contributors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | namespace SeqCli.Apps.Definitions; 16 | 17 | // Matches https://github.com/datalust/seq-apps-runtime/blob/dev/src/Seq.Apps/Apps/SettingInputType.cs 18 | public enum AppSettingType 19 | { 20 | Text, 21 | LongText, 22 | Checkbox, 23 | Integer, 24 | Decimal, 25 | Password, 26 | 27 | // Not mirrored in Seq.Apps; currently, only available to C# apps when the input type is left as 28 | // Unspecified, and the corresponding property is an enum type. 29 | Select, 30 | 31 | // Unused; required for (very early) legacy app support. 32 | Number = 1000 33 | } -------------------------------------------------------------------------------- /src/SeqCli/Apps/Definitions/AppPlatformDefinition.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Datalust Pty Ltd and Contributors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using Newtonsoft.Json; 16 | 17 | namespace SeqCli.Apps.Definitions; 18 | 19 | // ReSharper disable all 20 | class AppPlatformDefinition 21 | { 22 | [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] 23 | public string? Executable { get; set; } 24 | 25 | [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] 26 | public string? Arguments { get; set; } 27 | 28 | // The generic host for assembly-based apps uses this. 29 | [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] 30 | public string? SeqAppTypeName { get; set; } 31 | } -------------------------------------------------------------------------------- /src/SeqCli/Templates/Import/EntityTemplate.cs: -------------------------------------------------------------------------------- 1 | // Copyright © Datalust Pty Ltd and Contributors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using System; 16 | using SeqCli.Templates.Ast; 17 | 18 | namespace SeqCli.Templates.Import; 19 | 20 | class EntityTemplate 21 | { 22 | public string ResourceGroup { get; } 23 | public string Name { get; } 24 | public JsonTemplate Entity { get; } 25 | 26 | public EntityTemplate(string resourceGroup, string name, JsonTemplate entity) 27 | { 28 | ResourceGroup = resourceGroup ?? throw new ArgumentNullException(nameof(resourceGroup)); 29 | Name = name ?? throw new ArgumentNullException(nameof(name)); 30 | Entity = entity ?? throw new ArgumentNullException(nameof(entity)); 31 | } 32 | } -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Profile/ProfileCreateListRemoveTestCase.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Seq.Api; 3 | using SeqCli.EndToEnd.Support; 4 | using Serilog; 5 | using Xunit; 6 | 7 | namespace SeqCli.EndToEnd.Profile; 8 | 9 | public class ProfileCreateListRemoveTestCase : ICliTestCase 10 | { 11 | public Task ExecuteAsync(SeqConnection connection, ILogger logger, CliCommandRunner runner) 12 | { 13 | var create = runner.Exec("profile create", "-n Test -s https://seq.example.com -a 123", disconnected: true); 14 | Assert.Equal(0, create); 15 | 16 | Assert.Equal(0, runner.Exec("profile list", disconnected: true)); 17 | Assert.Contains("test (https://seq.example.com)", runner.LastRunProcess!.Output); 18 | 19 | Assert.Equal(0, runner.Exec("config", disconnected: true)); 20 | Assert.Contains("profiles[test].serverUrl", runner.LastRunProcess.Output); 21 | Assert.Contains("https://seq.example.com", runner.LastRunProcess.Output); 22 | Assert.Contains("profiles[test].apiKey", runner.LastRunProcess.Output); 23 | Assert.Contains("pd.", runner.LastRunProcess.Output); 24 | 25 | var remove = runner.Exec("profile remove", "-n Test", disconnected: true); 26 | Assert.Equal(0, remove); 27 | 28 | return Task.CompletedTask; 29 | } 30 | } -------------------------------------------------------------------------------- /test/SeqCli.Tests/Apps/AppContainerTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using Seq.Apps; 4 | using SeqCli.Apps.Hosting; 5 | using SeqCli.Tests.Support; 6 | using Serilog.Core; 7 | using Xunit; 8 | 9 | namespace SeqCli.Tests.Apps; 10 | 11 | public class AppContainerTests 12 | { 13 | [Fact] 14 | public void CanLoadOutputApp() 15 | { 16 | const string appBinaries = "Apps/FirstOfTypeBinaries"; 17 | Assert.True(Directory.Exists(appBinaries)); 18 | 19 | var appContainer = new AppContainer(Logger.None, appBinaries, 20 | new App(Some.String(), Some.String(), new Dictionary(), "./storage"), 21 | new Host(Some.UriString(), null)); 22 | 23 | appContainer.Dispose(); 24 | } 25 | 26 | [Fact] 27 | public void CanLoadInputApp() 28 | { 29 | const string appBinaries = "Apps/HealthCheckBinaries"; 30 | Assert.True(Directory.Exists(appBinaries)); 31 | 32 | var settings = new Dictionary { ["TargetUrl"] = Some.UriString() }; 33 | 34 | var appContainer = new AppContainer(Logger.None, appBinaries, 35 | new App(Some.String(), Some.String(), settings, "./storage"), 36 | new Host(Some.UriString(), null)); 37 | 38 | appContainer.Dispose(); 39 | } 40 | } -------------------------------------------------------------------------------- /src/SeqCli/Cli/Commands/VersionCommand.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Datalust Pty Ltd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using System; 16 | using System.Reflection; 17 | using System.Threading.Tasks; 18 | 19 | namespace SeqCli.Cli.Commands; 20 | 21 | [Command("version", "Print the current executable version")] 22 | class VersionCommand : Command 23 | { 24 | protected override Task Run() 25 | { 26 | var version = GetVersion(); 27 | Console.WriteLine(version); 28 | return Task.FromResult(0); 29 | } 30 | 31 | public static string GetVersion() 32 | { 33 | return typeof(VersionCommand).GetTypeInfo().Assembly 34 | .GetCustomAttribute()!.InformationalVersion; 35 | } 36 | } -------------------------------------------------------------------------------- /src/SeqCli/Forwarder/Filesystem/StoreFileAppender.cs: -------------------------------------------------------------------------------- 1 | // Copyright © Datalust Pty Ltd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using System; 16 | 17 | namespace SeqCli.Forwarder.Filesystem; 18 | 19 | abstract class StoreFileAppender : IDisposable 20 | { 21 | public abstract void Dispose(); 22 | 23 | /// 24 | /// Append the given data to the end of the file. 25 | /// 26 | public abstract void Append(Span data); 27 | 28 | /// 29 | /// Commit all appended data to underlying storage. 30 | /// 31 | public abstract long Commit(); 32 | 33 | /// 34 | /// Durably sync committed data to underlying storage. 35 | /// 36 | public abstract void Sync(); 37 | } 38 | -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Indexes/ExpressionIndexBasicsTestCase.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Threading.Tasks; 3 | using Seq.Api; 4 | using SeqCli.EndToEnd.Support; 5 | using Serilog; 6 | using Xunit; 7 | 8 | namespace SeqCli.EndToEnd.Indexes; 9 | 10 | [CliTestCase(MinimumApiVersion = "2024.3.0")] 11 | public class ExpressionIndexBasicsTestCase: ICliTestCase 12 | { 13 | public async Task ExecuteAsync(SeqConnection connection, ILogger logger, CliCommandRunner runner) 14 | { 15 | const string expr = "@Resource.service.name"; 16 | var exit = runner.Exec("expressionindex create", $"-e {expr}"); 17 | Assert.Equal(0, exit); 18 | 19 | var entity = (await connection.ExpressionIndexes.ListAsync()).Single(e => e.Expression == expr); 20 | Assert.Equal(expr, entity.Expression); 21 | 22 | exit = runner.Exec("expressionindex list"); 23 | Assert.Equal(0, exit); 24 | 25 | Assert.Contains(expr, runner.LastRunProcess!.Output); 26 | Assert.Contains(entity.Id, runner.LastRunProcess.Output); 27 | 28 | exit = runner.Exec("expressionindex remove", $"-i {entity.Id}"); 29 | Assert.Equal(0, exit); 30 | 31 | exit = runner.Exec("expressionindex list"); 32 | Assert.Equal(0, exit); 33 | 34 | Assert.DoesNotContain(entity.Id, runner.LastRunProcess.Output); 35 | } 36 | } -------------------------------------------------------------------------------- /src/Roastery/Data/DatabaseMigrator.cs: -------------------------------------------------------------------------------- 1 | using Roastery.Model; 2 | 3 | namespace Roastery.Data; 4 | 5 | static class DatabaseMigrator 6 | { 7 | public static void Populate(Database database) 8 | { 9 | database.BulkLoad( 10 | new Product("Rocket Ship Dark Roast, Whole Beans", 100) {Id = "product-8908fd0sa"}, 11 | new Product("Rocket Ship Dark Roast, Whole Beans", 250) {Id = "product-fsad890fj"}, 12 | new Product("Rocket Ship Dark Roast, Whole Beans", 1000) {Id = "product-fsdjkljrw"}, 13 | new Product("Rocket Ship Dark Roast, Ground", 100) {Id = "product-2nkfkdsju"}, 14 | new Product("Rocket Ship Dark Roast, Ground", 250) {Id = "product-f8sa9newq"}, 15 | new Product("Rocket Ship Dark Roast, Ground", 1000) {Id = "product-cvsad9033"}, 16 | new Product("1 AM Medium Roast, Whole Beans", 100) {Id = "product-i908jd0sf"}, 17 | new Product("1 AM Medium Roast, Whole Beans", 250) {Id = "product-isadj90fd"}, 18 | new Product("1 AM Medium Roast, Whole Beans", 1000) {Id = "product-isdjjljr3"}, 19 | new Product("1 AM Medium Roast, Ground", 100) {Id = "product-inkfjdsj2"}, 20 | new Product("1 AM Medium Roast, Ground", 250) {Id = "product-i8sajnew1"}, 21 | new Product("1 AM Medium Roast, Ground", 1000) {Id = "product-ivsaj903t"}); 22 | } 23 | } -------------------------------------------------------------------------------- /src/SeqCli/Ingestion/SendFailureHandling.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Datalust Pty Ltd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | namespace SeqCli.Ingestion; 16 | 17 | /// 18 | /// Controls how connection failures during ingestion are handled. 19 | /// 20 | enum SendFailureHandling 21 | { 22 | /// 23 | /// Log error information and exit. 24 | /// 25 | Fail, 26 | 27 | /// 28 | /// Log error information, drop the failed batch, and continue. 29 | /// 30 | Continue, 31 | 32 | /// 33 | /// Silently ignore failures. 34 | /// 35 | Ignore, 36 | 37 | /// 38 | /// Log error information, wait for up to a minute, then retry. 39 | /// 40 | Retry 41 | } -------------------------------------------------------------------------------- /src/SeqCli/Cli/Features/ConfigValueFeature.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SeqCli.Cli.Features; 4 | 5 | class ConfigValueFeature: CommandFeature 6 | { 7 | // An empty string is normalized to null/unset, which will normally be considered "cleared"; we allow this 8 | // to keep the CLI backwards-compatible. 9 | bool _valueSpecified; 10 | 11 | string? Value { get; set; } 12 | bool ReadValueFromStdin { get; set; } 13 | 14 | public override void Enable(OptionSet options) 15 | { 16 | options.Add("v|value=", 17 | "The field value, comma-separated if multiple values are accepted", 18 | v => 19 | { 20 | _valueSpecified = true; 21 | // Not normalized; some settings might include leading/trailing whitespace. 22 | Value = v; 23 | }); 24 | 25 | options.Add("value-stdin", 26 | "Read the value from `STDIN`", 27 | _ => ReadValueFromStdin = true); 28 | } 29 | 30 | public string? ReadValue() 31 | { 32 | if (!_valueSpecified && !ReadValueFromStdin) 33 | throw new ArgumentException( 34 | "A value must be supplied with either `--value=VALUE` or `--value-stdin`."); 35 | 36 | return ReadValueFromStdin ? Console.In.ReadToEnd().TrimEnd('\r', '\n') : Value; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/SeqCli/Templates/Parser/JsonTemplateToken.cs: -------------------------------------------------------------------------------- 1 | // Copyright © Datalust Pty Ltd and Contributors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using Superpower.Display; 16 | 17 | namespace SeqCli.Templates.Parser; 18 | 19 | enum JsonTemplateToken 20 | { 21 | [Token(Example = "{")] 22 | LBracket, 23 | 24 | [Token(Example = "}")] 25 | RBracket, 26 | 27 | [Token(Example = "[")] 28 | LSquareBracket, 29 | 30 | [Token(Example = "]")] 31 | RSquareBracket, 32 | 33 | [Token(Example = ":")] 34 | Colon, 35 | 36 | [Token(Example = ",")] 37 | Comma, 38 | 39 | String, 40 | 41 | Number, 42 | 43 | [Token(Example = "(")] 44 | LParen, 45 | 46 | [Token(Example = ")")] 47 | RParen, 48 | 49 | Identifier, 50 | } -------------------------------------------------------------------------------- /test/SeqCli.Tests/SeqCli.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net9.0 4 | 5 | 6 | 7 | 8 | 9 | all 10 | runtime; build; native; contentfiles; analyzers; buildtransitive 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | PreserveNewest 22 | 23 | 24 | PreserveNewest 25 | 26 | 27 | PreserveNewest 28 | 29 | 30 | PreserveNewest 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/SeqCli/Config/RuntimeConfigurationLoader.cs: -------------------------------------------------------------------------------- 1 | // Copyright © Datalust Pty Ltd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using SeqCli.Cli.Features; 16 | 17 | namespace SeqCli.Config; 18 | 19 | static class RuntimeConfigurationLoader 20 | { 21 | const string DefaultEnvironmentVariablePrefix = "SEQCLI_"; 22 | 23 | /// 24 | /// This is the method to use when loading configuration for runtime use. It will read the default configuration 25 | /// file, if any, and apply overrides from the environment. 26 | /// 27 | public static SeqCliConfig Load(StoragePathFeature storage) 28 | { 29 | var config = SeqCliConfig.ReadFromFile(storage.ConfigFilePath); 30 | 31 | EnvironmentOverrides.Apply(DefaultEnvironmentVariablePrefix, config); 32 | 33 | return config; 34 | } 35 | } -------------------------------------------------------------------------------- /test/SeqCli.Tests/Support/TempFolder.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | 3 | using System; 4 | using System.Diagnostics; 5 | using System.IO; 6 | using System.Runtime.CompilerServices; 7 | 8 | namespace SeqCli.Tests.Support; 9 | 10 | class TempFolder : IDisposable 11 | { 12 | static readonly Guid Session = Guid.NewGuid(); 13 | 14 | public TempFolder(string name) 15 | { 16 | Path = System.IO.Path.Combine( 17 | Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), 18 | "SeqCli.Tests", 19 | Session.ToString("n"), 20 | name); 21 | 22 | Directory.CreateDirectory(Path); 23 | } 24 | 25 | public string Path { get; } 26 | 27 | public void Dispose() 28 | { 29 | try 30 | { 31 | if (Directory.Exists(Path)) 32 | Directory.Delete(Path, true); 33 | } 34 | catch (Exception ex) 35 | { 36 | Debug.WriteLine(ex); 37 | } 38 | } 39 | 40 | public static TempFolder ForCaller([CallerMemberName] string? caller = null) 41 | { 42 | if (caller == null) throw new ArgumentNullException(nameof(caller)); 43 | return new TempFolder(caller); 44 | } 45 | 46 | public string AllocateFilename(string? ext = null) 47 | { 48 | return System.IO.Path.Combine(Path, Guid.NewGuid().ToString("n") + "." + (ext ?? "tmp")); 49 | } 50 | } -------------------------------------------------------------------------------- /src/SeqCli/Cli/Commands/ExpressionIndex/ListCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using SeqCli.Api; 4 | using SeqCli.Cli.Features; 5 | using SeqCli.Config; 6 | 7 | namespace SeqCli.Cli.Commands.ExpressionIndex; 8 | 9 | [Command("expressionindex", "list", "List expression indexes", Example="seqcli expressionindex list")] 10 | class ListCommand : Command 11 | { 12 | readonly ConnectionFeature _connection; 13 | readonly OutputFormatFeature _output; 14 | readonly StoragePathFeature _storagePath; 15 | 16 | string? _id; 17 | 18 | public ListCommand() 19 | { 20 | Options.Add( 21 | "i=|id=", 22 | "The id of a single expression index to list", 23 | id => _id = id); 24 | 25 | _output = Enable(); 26 | _storagePath = Enable(); 27 | _connection = Enable(); 28 | } 29 | 30 | protected override async Task Run() 31 | { 32 | var config = RuntimeConfigurationLoader.Load(_storagePath); 33 | var connection = SeqConnectionFactory.Connect(_connection, config); 34 | var list = _id is not null 35 | ? [await connection.ExpressionIndexes.FindAsync(_id)] 36 | : await connection.ExpressionIndexes.ListAsync(); 37 | _output.GetOutputFormat(config).ListEntities(list); 38 | return 0; 39 | } 40 | } -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Settings/SettingBasicsTestCase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Seq.Api; 4 | using SeqCli.EndToEnd.Support; 5 | using Serilog; 6 | using Xunit; 7 | 8 | namespace SeqCli.EndToEnd.Settings; 9 | 10 | public class SettingBasicsTestCase : ICliTestCase 11 | { 12 | public Task ExecuteAsync( 13 | SeqConnection connection, 14 | ILogger logger, 15 | CliCommandRunner runner) 16 | { 17 | var exit = runner.Exec("setting names"); 18 | Assert.Equal(0, exit); 19 | Assert.Contains("InstanceTitle", runner.LastRunProcess!.Output); 20 | 21 | exit = runner.Exec("setting show", "-n instancetitle"); 22 | Assert.Equal(0, exit); 23 | Assert.Empty(runner.LastRunProcess.Output); 24 | 25 | exit = runner.Exec("setting set", "-n instancetitle -v \"Hello, world!\""); 26 | Assert.Equal(0, exit); 27 | 28 | exit = runner.Exec("setting show", "-n instancetitle"); 29 | Assert.Equal(0, exit); 30 | Assert.Equal("Hello, world!", runner.LastRunProcess.Output.Trim()); 31 | 32 | exit = runner.Exec("setting clear", "-n instancetitle"); 33 | Assert.Equal(0, exit); 34 | 35 | exit = runner.Exec("setting show", "-n instancetitle"); 36 | Assert.Equal(0, exit); 37 | Assert.Empty(runner.LastRunProcess.Output); 38 | 39 | return Task.CompletedTask; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Indexes/IndexesTestCase.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Threading.Tasks; 3 | using Seq.Api; 4 | using SeqCli.EndToEnd.Support; 5 | using Serilog; 6 | using Xunit; 7 | 8 | namespace SeqCli.EndToEnd.Indexes; 9 | 10 | [CliTestCase(MinimumApiVersion = "2024.3.0")] 11 | public class IndexesTestCase: ICliTestCase 12 | { 13 | public async Task ExecuteAsync(SeqConnection connection, ILogger logger, CliCommandRunner runner) 14 | { 15 | const string expr = "Magic123"; 16 | var exit = runner.Exec("expressionindex create", $"-e {expr}"); 17 | Assert.Equal(0, exit); 18 | 19 | var expressionIndex = (await connection.ExpressionIndexes.ListAsync()).Single(e => e.Expression == expr); 20 | var signal = (await connection.Signals.ListAsync(shared: true)).First(s => !s.IsIndexSuppressed); 21 | var indexForSignal = (await connection.Indexes.ListAsync()).First(i => i.IndexedEntityId == signal.Id); 22 | 23 | exit = runner.Exec("index list --json"); 24 | Assert.Equal(0, exit); 25 | Assert.Contains(expressionIndex.Id, runner.LastRunProcess!.Output); 26 | Assert.Contains(signal.Id, runner.LastRunProcess!.Output); 27 | 28 | exit = runner.Exec($"index suppress -i {indexForSignal.Id}"); 29 | Assert.Equal(0, exit); 30 | 31 | signal = await connection.Signals.FindAsync(signal.Id); 32 | Assert.True(signal.IsIndexSuppressed); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/SeqCli/PlainText/Extraction/NameValueExtractor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Superpower; 5 | using Superpower.Model; 6 | using Superpower.Parsers; 7 | 8 | namespace SeqCli.PlainText.Extraction; 9 | 10 | class NameValueExtractor 11 | { 12 | readonly PatternElement[] _elements; 13 | 14 | public NameValueExtractor(IEnumerable elements) 15 | { 16 | _elements = elements?.ToArray() ?? throw new ArgumentNullException(nameof(elements)); 17 | if (_elements.Length == 0) 18 | throw new ArgumentException("An extraction pattern must contain at least one element."); 19 | } 20 | 21 | public TextParser StartMarker => _elements[0].Match; 22 | 23 | public (IDictionary, string?) ExtractValues(string plainText) 24 | { 25 | var input = new TextSpan(plainText); 26 | var result = new Dictionary(); 27 | 28 | var remainder = input; 29 | foreach (var element in _elements) 30 | { 31 | if (!element.TryExtract(remainder, result, out remainder)) 32 | { 33 | if (remainder.IsAtEnd || Span.WhiteSpace.IsMatch(remainder)) 34 | return (result, null); 35 | 36 | return (result, remainder.ToStringValue()); 37 | } 38 | } 39 | 40 | return (result, null); 41 | } 42 | } -------------------------------------------------------------------------------- /test/SeqCli.Tests/Apps/AppDefinitionFormatterTests.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.IO; 3 | using Seq.Apps; 4 | using SeqCli.Apps.Definitions; 5 | using Serilog.Events; 6 | using Xunit; 7 | 8 | // ReSharper disable all 9 | 10 | namespace SeqCli.Tests.Apps; 11 | 12 | public class AppDefinitionFormatterTests 13 | { 14 | enum DomesticAnimal 15 | { 16 | Cat, 17 | Dog, 18 | Goldfish, 19 | [Description("Exotic species")] 20 | ExoticSpecies 21 | } 22 | 23 | [SeqApp("Example App", Description = "Just for this test!")] 24 | class ExampleApp : SeqApp, ISubscribeTo 25 | { 26 | [SeqAppSetting(DisplayName = "Pet name")] 27 | public string PetName { get; set; } 28 | 29 | [SeqAppSetting(HelpText = "The species of your pet.", IsOptional = true)] 30 | public DomesticAnimal? Species { get; set; } 31 | 32 | public void On(Event evt) 33 | { 34 | } 35 | } 36 | 37 | [Fact] 38 | public void FormatsDefinitions() 39 | { 40 | var expected = File.ReadAllText("Apps/ExampleApp.d.json"); 41 | var formatted = new StringWriter(); 42 | AppDefinitionFormatter.FormatAppDefinition(typeof(ExampleApp), true, formatted); 43 | Assert.Equal(Normalize(expected), Normalize(formatted.ToString())); 44 | } 45 | 46 | static string Normalize(string s) 47 | { 48 | return s.Trim().Replace("\r", ""); 49 | } 50 | } -------------------------------------------------------------------------------- /src/SeqCli/Syntax/CombinedFilterBuilder.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Datalust Pty Ltd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using System; 16 | using System.Collections.Generic; 17 | using System.Linq; 18 | 19 | namespace SeqCli.Syntax; 20 | 21 | class CombinedFilterBuilder 22 | { 23 | readonly List _elements = new(); 24 | 25 | public CombinedFilterBuilder Intersect(string? filter) 26 | { 27 | if (filter == null) 28 | return this; 29 | 30 | if (string.IsNullOrWhiteSpace(filter)) 31 | throw new ArgumentNullException(nameof(filter)); 32 | 33 | _elements.Add(filter.Trim()); 34 | return this; 35 | } 36 | 37 | public string? Build() 38 | { 39 | if (!_elements.Any()) 40 | return null; 41 | 42 | if (_elements.Count == 1) 43 | return _elements.Single(); 44 | 45 | return "(" + string.Join(")and(", _elements) + ")"; 46 | } 47 | } -------------------------------------------------------------------------------- /src/SeqCli/Cli/Commands/AppInstance/ListCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using SeqCli.Api; 5 | using SeqCli.Cli.Features; 6 | using SeqCli.Config; 7 | 8 | namespace SeqCli.Cli.Commands.AppInstance; 9 | 10 | [Command("appinstance", "list", "List instances of installed apps", Example="seqcli appinstance list")] 11 | class ListCommand : Command 12 | { 13 | readonly EntityIdentityFeature _entityIdentity; 14 | readonly ConnectionFeature _connection; 15 | readonly OutputFormatFeature _output; 16 | readonly StoragePathFeature _storagePath; 17 | 18 | public ListCommand() 19 | { 20 | _entityIdentity = Enable(new EntityIdentityFeature("app instance", "list")); 21 | _output = Enable(); 22 | _storagePath = Enable(); 23 | _connection = Enable(); 24 | } 25 | 26 | protected override async Task Run() 27 | { 28 | var config = RuntimeConfigurationLoader.Load(_storagePath); 29 | var connection = SeqConnectionFactory.Connect(_connection, config); 30 | 31 | var list = _entityIdentity.Id != null ? [await connection.AppInstances.FindAsync(_entityIdentity.Id)] 32 | : 33 | (await connection.AppInstances.ListAsync()) 34 | .Where(d => _entityIdentity.Title == null || _entityIdentity.Title == d.Title); 35 | 36 | _output.GetOutputFormat(config).ListEntities(list); 37 | 38 | return 0; 39 | } 40 | } -------------------------------------------------------------------------------- /src/SeqCli/Cli/Commands/License/ShowCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | using Seq.Api.Model; 6 | using Seq.Api.Model.License; 7 | using SeqCli.Api; 8 | using SeqCli.Cli.Features; 9 | using SeqCli.Config; 10 | using SeqCli.Util; 11 | using Serilog; 12 | 13 | // ReSharper disable once UnusedType.Global 14 | 15 | namespace SeqCli.Cli.Commands.License; 16 | 17 | [Command("license", "show", "Shows license applied to the Seq server", 18 | Example = "seqcli license show")] 19 | class ShowCommand : Command 20 | { 21 | readonly ConnectionFeature _connection; 22 | readonly OutputFormatFeature _output; 23 | readonly StoragePathFeature _storage; 24 | 25 | public ShowCommand() 26 | { 27 | _connection = Enable(); 28 | _output = Enable(); 29 | _storage = Enable(); 30 | } 31 | 32 | protected override async Task Run() 33 | { 34 | var config = RuntimeConfigurationLoader.Load(_storage); 35 | var output = _output.GetOutputFormat(config); 36 | 37 | var connection = SeqConnectionFactory.Connect(_connection, config); 38 | var license = await connection.Licenses.FindCurrentAsync(); 39 | 40 | if (output.Json) 41 | { 42 | output.WriteEntity(license); 43 | } 44 | else 45 | { 46 | output.WriteText(license?.LicenseText); 47 | } 48 | 49 | return 0; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/SeqCli/Cli/Printing.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Datalust Pty Ltd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using System.IO; 16 | using System.Linq; 17 | 18 | namespace SeqCli.Cli; 19 | 20 | static class Printing 21 | { 22 | const int ConsoleWidth = 82, TermColumnWidth = 14; 23 | 24 | public static void Define(string term, string definition, TextWriter output) 25 | { 26 | var header = term.PadRight(TermColumnWidth); 27 | var right = ConsoleWidth - header.Length; 28 | 29 | var rest = definition.ToCharArray(); 30 | while (rest.Any()) 31 | { 32 | var content = new string(rest.Take(right).ToArray()); 33 | if (!string.IsNullOrWhiteSpace(content)) 34 | { 35 | output.Write(header); 36 | header = new string(' ', header.Length); 37 | output.WriteLine(content); 38 | } 39 | rest = rest.Skip(right).ToArray(); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/SeqCli/Cli/CommandAttribute.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Datalust Pty Ltd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using System; 16 | 17 | namespace SeqCli.Cli; 18 | 19 | [AttributeUsage(AttributeTargets.Class)] 20 | public class CommandAttribute : Attribute, ICommandMetadata 21 | { 22 | public string Name { get; } 23 | public string? SubCommand { get; } 24 | public string HelpText { get; } 25 | public string? Example { get; set; } 26 | public FeatureVisibility Visibility { get; set; } 27 | public SupportedPlatforms Platforms { get; set; } 28 | 29 | public CommandAttribute(string name, string helpText) 30 | { 31 | Name = name; 32 | HelpText = helpText; 33 | Visibility = FeatureVisibility.Visible; 34 | Platforms = SupportedPlatforms.All; 35 | } 36 | 37 | public CommandAttribute(string name, string subCommand, string helpText) : this(name, helpText) 38 | { 39 | SubCommand = subCommand; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/SeqCli/Cli/Commands/Config/ListCommand.cs: -------------------------------------------------------------------------------- 1 | // Copyright © Datalust Pty Ltd and Contributors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using System; 16 | using System.Threading.Tasks; 17 | using SeqCli.Cli.Features; 18 | using SeqCli.Config; 19 | 20 | // ReSharper disable once UnusedType.Global 21 | 22 | namespace SeqCli.Cli.Commands.Config; 23 | 24 | [Command("config", "list", "View all fields in the `SeqCli.json` file")] 25 | class ListCommand : Command 26 | { 27 | readonly StoragePathFeature _storagePath; 28 | 29 | public ListCommand() 30 | { 31 | _storagePath = Enable(); 32 | } 33 | 34 | protected override Task Run() 35 | { 36 | var config = SeqCliConfig.ReadFromFile(_storagePath.ConfigFilePath); 37 | foreach (var (key, value, _) in KeyValueSettings.Inspect(config)) 38 | { 39 | Console.WriteLine($"{key}={value}"); 40 | } 41 | return Task.FromResult(0); 42 | } 43 | } -------------------------------------------------------------------------------- /src/SeqCli/Forwarder/Diagnostics/InMemorySink.cs: -------------------------------------------------------------------------------- 1 | // Copyright © Datalust Pty Ltd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using System; 16 | using System.Collections.Concurrent; 17 | using System.Collections.Generic; 18 | using Serilog.Core; 19 | using Serilog.Events; 20 | 21 | namespace SeqCli.Forwarder.Diagnostics; 22 | 23 | class InMemorySink : ILogEventSink 24 | { 25 | readonly int _queueLength; 26 | readonly ConcurrentQueue _queue = new(); 27 | 28 | public InMemorySink(int queueLength) 29 | { 30 | _queueLength = queueLength; 31 | } 32 | 33 | public IEnumerable Read() 34 | { 35 | return _queue.ToArray(); 36 | } 37 | 38 | public void Emit(LogEvent logEvent) 39 | { 40 | ArgumentNullException.ThrowIfNull(logEvent); 41 | _queue.Enqueue(logEvent); 42 | 43 | while (_queue.Count > _queueLength) 44 | { 45 | _queue.TryDequeue(out _); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /src/SeqCli/Sample/Templates/workspace-Sample.template: -------------------------------------------------------------------------------- 1 | { 2 | "$entity": "workspace", 3 | "OwnerId": arg("ownerId"), 4 | "Title": "Sample", 5 | "Description": "Created by `seqcli sample setup`", 6 | "IsProtected": false, 7 | "DefaultSignalExpression": { 8 | "SignalId": ref("signal-Sample Data.template"), 9 | "Kind": "Signal" 10 | }, 11 | "Content": { 12 | "DashboardIds": [ 13 | ref("dashboard-Database.template"), 14 | ref("dashboard-HTTP Requests.template"), 15 | ref("dashboard-Orders.template") 16 | ], 17 | "QueryIds": [ 18 | ref("sqlquery-Order Items by Product Size.template"), 19 | ref("sqlquery-Route Bindings.template") 20 | ], 21 | "SignalIds": [ 22 | "signal-m33301", 23 | "signal-m33302", 24 | "signal-m33303", 25 | ref("signal-Bad Request.template"), 26 | ref("signal-Batch Processing.template"), 27 | ref("signal-Database.template"), 28 | ref("signal-HTTP Requests.template"), 29 | ref("signal-Information.template"), 30 | ref("signal-Internal Server Error.template"), 31 | ref("signal-Not Found.template"), 32 | ref("signal-Order Abandoned.template"), 33 | ref("signal-Order Archived.template"), 34 | ref("signal-Order Created.template"), 35 | ref("signal-Order Placed.template"), 36 | ref("signal-Order Shipped.template"), 37 | "signal-29", 38 | ref("signal-Stock Level Warnings.template"), 39 | ref("signal-Success.template"), 40 | ref("signal-Web Frontend.template") 41 | ] 42 | } 43 | } -------------------------------------------------------------------------------- /test/SeqCli.EndToEnd/Forwarder/ForwarderSimpleIngestionTestCase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Threading.Tasks; 4 | using Seq.Api; 5 | using SeqCli.EndToEnd.Support; 6 | using Serilog; 7 | using Xunit; 8 | 9 | namespace SeqCli.EndToEnd.Forwarder; 10 | 11 | public class ForwarderSimpleIngestionTestCase: ICliTestCase 12 | { 13 | public async Task ExecuteAsync(SeqConnection connection, ILogger logger, CliCommandRunner runner) 14 | { 15 | var (forwarder, forwarderUri) = await runner.SpawnForwarderAsync(); 16 | using (forwarder) 17 | { 18 | var ingestionLogger = new LoggerConfiguration() 19 | .WriteTo.Seq(forwarderUri) 20 | .CreateLogger(); 21 | 22 | const int itemCount = 1032; 23 | for (var i = 0; i < itemCount; ++i) 24 | { 25 | ingestionLogger.ForContext("Ballast", new string('a', 51)) 26 | .Information("At item {I}", i); 27 | } 28 | 29 | // In recent versions this should be sufficient to flush any queued events. 30 | await ingestionLogger.DisposeAsync(); 31 | 32 | // Give forwarder enough time to move data... 33 | await Task.Delay(TimeSpan.FromSeconds(5)); 34 | 35 | var result = await connection.Data.QueryAsync("select count(*) from stream"); 36 | var retrievedCount = (long)result.Rows[0][0]; 37 | 38 | Assert.Equal(itemCount, retrievedCount); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /test/SeqCli.Tests/Forwarder/Filesystem/InMemoryStoreDirectory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using SeqCli.Forwarder.Filesystem; 5 | 6 | namespace SeqCli.Tests.Forwarder.Filesystem; 7 | 8 | class InMemoryStoreDirectory : StoreDirectory 9 | { 10 | readonly Dictionary _files = new(); 11 | 12 | public IReadOnlyDictionary Files => _files; 13 | 14 | public override InMemoryStoreFile Create(string name) 15 | { 16 | if (_files.ContainsKey(name)) throw new Exception($"The file {name} already exists."); 17 | 18 | _files.Add(name, new InMemoryStoreFile()); 19 | 20 | return _files[name]; 21 | } 22 | 23 | public InMemoryStoreFile Create(string name, Span contents) 24 | { 25 | var file = Create(name); 26 | file.Append(contents); 27 | 28 | return file; 29 | } 30 | 31 | public override bool TryDelete(string name) 32 | { 33 | return _files.Remove(name); 34 | } 35 | 36 | public override InMemoryStoreFile Replace(string toReplace, string replaceWith) 37 | { 38 | _files[toReplace] = _files[replaceWith]; 39 | _files.Remove(replaceWith); 40 | 41 | return _files[toReplace]; 42 | } 43 | 44 | public override IEnumerable<(string Name, StoreFile File)> List(Func predicate) 45 | { 46 | return _files 47 | .Where(kv => predicate(kv.Key)) 48 | .Select(kv => (kv.Key, kv.Value as StoreFile)); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/SeqCli/Cli/Features/SignalExpressionFeature.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Datalust Pty Ltd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using Seq.Api.Model.Signals; 16 | 17 | namespace SeqCli.Cli.Features; 18 | 19 | class SignalExpressionFeature : CommandFeature 20 | { 21 | string? _signalExpression; 22 | 23 | public SignalExpressionPart? Signal 24 | { 25 | get 26 | { 27 | if (string.IsNullOrWhiteSpace(_signalExpression)) 28 | return null; 29 | 30 | // This is a hack that just happens to work because of the way 31 | // signal ids are passed through ToString() as literals 32 | return SignalExpressionPart.Signal(_signalExpression.Trim()); 33 | } 34 | } 35 | 36 | public override void Enable(OptionSet options) 37 | { 38 | options.Add( 39 | "signal=", 40 | "A signal expression or list of intersected signal ids to apply, for example `signal-1,signal-2`", 41 | v => _signalExpression = v); 42 | } 43 | } -------------------------------------------------------------------------------- /src/SeqCli/Cli/Commands/Config/ClearCommand.cs: -------------------------------------------------------------------------------- 1 | // Copyright © Datalust Pty Ltd and Contributors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using System.Threading.Tasks; 16 | using SeqCli.Cli.Features; 17 | using SeqCli.Config; 18 | 19 | // ReSharper disable once UnusedType.Global 20 | 21 | namespace SeqCli.Cli.Commands.Config; 22 | 23 | [Command("config", "clear", "Clear fields in the `SeqCli.json` file")] 24 | class ClearCommand : Command 25 | { 26 | readonly StoragePathFeature _storagePath; 27 | readonly ConfigKeyFeature _key; 28 | 29 | public ClearCommand() 30 | { 31 | _storagePath = Enable(); 32 | _key = Enable(); 33 | } 34 | 35 | protected override Task Run() 36 | { 37 | var config = SeqCliConfig.ReadFromFile(_storagePath.ConfigFilePath); 38 | 39 | KeyValueSettings.Clear(config, _key.GetKey()); 40 | SeqCliConfig.WriteToFile(config, _storagePath.ConfigFilePath); 41 | return Task.FromResult(0); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Roastery/Web/HttpClient.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http; 2 | using System.Threading.Tasks; 3 | 4 | namespace Roastery.Web; 5 | 6 | class HttpClient 7 | { 8 | readonly string _basePath; 9 | readonly HttpServer _server; 10 | 11 | public HttpClient(string basePath, HttpServer server) 12 | { 13 | _basePath = basePath.TrimEnd('/') + '/'; 14 | _server = server; 15 | } 16 | 17 | public async Task GetAsync(string path) 18 | { 19 | var request = new HttpRequest(HttpMethod.Get, _basePath + path); 20 | var response = await _server.InvokeAsync(request); 21 | response.EnsureSuccessStatusCode(); 22 | return (TResponse)response.Body!; 23 | } 24 | 25 | public async Task PutAsync(string path, object body) 26 | { 27 | var request = new HttpRequest(HttpMethod.Put, _basePath + path, body); 28 | var response = await _server.InvokeAsync(request); 29 | response.EnsureSuccessStatusCode(); 30 | } 31 | 32 | public async Task PostAsync(string path, object body) 33 | { 34 | var request = new HttpRequest(HttpMethod.Post, _basePath + path, body); 35 | var response = await _server.InvokeAsync(request); 36 | response.EnsureSuccessStatusCode(); 37 | return (TResponse) response.Body!; 38 | } 39 | 40 | public async Task DeleteAsync(string path) 41 | { 42 | var request = new HttpRequest(HttpMethod.Delete, _basePath + path); 43 | var response = await _server.InvokeAsync(request); 44 | response.EnsureSuccessStatusCode(); 45 | } 46 | } --------------------------------------------------------------------------------