diff --git a/README.md b/README.md index de186132..f9741e0b 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,12 @@ View and set fields in the `SeqCli.json` file; run with no arguments to list all Show information about available commands. +Example: + +``` +seqcli help search +``` + | Option | Description | | ------ | ----------- | | `-m`, `--markdown` | Generate markdown for use in documentation | @@ -98,6 +104,8 @@ seqcli query -q "select count(*) from stream group by @Level" --start="2018-02-2 | `--end=VALUE` | Date/time to query to | | `--signal=VALUE` | A signal expression or list of intersected signal ids to apply, for example `signal-1,signal-2` | | `--timeout=VALUE` | The query execution timeout in milliseconds | +| `--json` | Print events in newline-delimited JSON (the default is plain text) | +| `--no-color` | Don't colorize text output | | `-s`, `--server=VALUE` | The URL of the Seq server; by default the `connection.serverUrl` value will be used | | `-a`, `--apikey=VALUE` | The API key to use when connecting to the server; by default `config.apiKey` value will be used | diff --git a/seqcli.sln b/seqcli.sln index d1af2409..7c8ae275 100644 --- a/seqcli.sln +++ b/seqcli.sln @@ -47,7 +47,6 @@ Global {DBC69360-519B-4A9B-829B-2AE1B5412521}.Release|x64.ActiveCfg = Release|x64 {DBC69360-519B-4A9B-829B-2AE1B5412521}.Release|x64.Build.0 = Release|x64 {5E28D963-3523-49DE-B03B-E76684258415}.Debug|x64.ActiveCfg = Debug|x64 - {5E28D963-3523-49DE-B03B-E76684258415}.Debug|x64.Build.0 = Debug|x64 {5E28D963-3523-49DE-B03B-E76684258415}.Release|x64.ActiveCfg = Release|x64 {5E28D963-3523-49DE-B03B-E76684258415}.Release|x64.Build.0 = Release|x64 EndGlobalSection diff --git a/src/SeqCli/Cli/Commands/HelpCommand.cs b/src/SeqCli/Cli/Commands/HelpCommand.cs index 27676646..dda56ae8 100644 --- a/src/SeqCli/Cli/Commands/HelpCommand.cs +++ b/src/SeqCli/Cli/Commands/HelpCommand.cs @@ -21,7 +21,7 @@ namespace SeqCli.Cli.Commands { - [Command("help", "Show information about available commands")] + [Command("help", "Show information about available commands", Example = "seqcli help search")] class HelpCommand : Command { readonly List, CommandMetadata>> _availableCommands; diff --git a/src/SeqCli/Cli/Commands/QueryCommand.cs b/src/SeqCli/Cli/Commands/QueryCommand.cs index 620efc04..018c698c 100644 --- a/src/SeqCli/Cli/Commands/QueryCommand.cs +++ b/src/SeqCli/Cli/Commands/QueryCommand.cs @@ -20,7 +20,6 @@ using SeqCli.Cli.Features; using SeqCli.Config; using SeqCli.Connection; -using SeqCli.Csv; using Serilog; namespace SeqCli.Cli.Commands @@ -29,23 +28,23 @@ namespace SeqCli.Cli.Commands Example = "seqcli query -q \"select count(*) from stream group by @Level\" --start=\"2018-02-28T13:00Z\"")] class QueryCommand : Command { + readonly OutputFormatFeature _output; readonly SeqConnectionFactory _connectionFactory; readonly ConnectionFeature _connection; readonly DateRangeFeature _range; readonly SignalExpressionFeature _signal; string _query; int? _timeoutMS; - bool _noColor; public QueryCommand(SeqConnectionFactory connectionFactory, SeqCliConfig config) { + if (config == null) throw new ArgumentNullException(nameof(config)); _connectionFactory = connectionFactory ?? throw new ArgumentNullException(nameof(connectionFactory)); Options.Add("q=|query=", "The query to execute", v => _query = v); _range = Enable(); _signal = Enable(); Options.Add("timeout=", "The query execution timeout in milliseconds", v => _timeoutMS = int.Parse(v)); - Options.Add("no-color", "Don't colorize text output", v => _noColor = true); - _noColor = config.Output.DisableColor; + _output = Enable(new OutputFormatFeature(config.Output)); _connection = Enable(); } @@ -59,32 +58,41 @@ protected override async Task Run() var connection = _connectionFactory.Connect(_connection); - // The `rangeStartUtc` parameter of `QueryCsvAsync()` should now be optional; we can + // The `rangeStartUtc` parameter of `Query[Csv]Async()` should now be optional; we can // remove the `.Value` when _Seq.Api_ is updated to reflect this. // ReSharper disable once PossibleInvalidOperationException - var result = await QueryCsvAsync(connection, _query, _range.Start, _range.End, _signal.Signal, _timeoutMS); - if (_noColor) + if (_output.Json) { - Console.Write(result); - return 0; + var result = await QueryAsync(connection, _query, _range.Start, _range.End, _signal.Signal, _timeoutMS); + Console.WriteLine(result); + } + else + { + var result = await QueryAsync(connection, _query, _range.Start, _range.End, _signal.Signal, _timeoutMS, "text/csv"); + _output.WriteCsv(result); } - - var tokens = new CsvTokenizer().Tokenize(result); - CsvWriter.WriteCsv(tokens, OutputFormatFeature.ConsoleTheme, Console.Out, true); return 0; } - static async Task QueryCsvAsync(SeqConnection connection, string query, DateTime? rangeStartUtc, DateTime? rangeEndUtc, SignalExpressionPart signalExpression, int? timeoutMS) + static async Task QueryAsync( + SeqConnection connection, + string query, + DateTime? rangeStartUtc, + DateTime? rangeEndUtc, + SignalExpressionPart signalExpression, + int? timeoutMS, + string format = null) { // From dates should no longer be mandatory for QueryCsvAsync (issue raised) var parameters = new Dictionary { - ["q"] = query, - ["format"] = "text/csv" + ["q"] = query }; + if (format != null) + parameters.Add(nameof(format), format); if (rangeStartUtc.HasValue) parameters.Add(nameof(rangeEndUtc), rangeStartUtc.Value); if (rangeEndUtc.HasValue) diff --git a/src/SeqCli/Cli/Features/OutputFormatFeature.cs b/src/SeqCli/Cli/Features/OutputFormatFeature.cs index e483aacb..506b231b 100644 --- a/src/SeqCli/Cli/Features/OutputFormatFeature.cs +++ b/src/SeqCli/Cli/Features/OutputFormatFeature.cs @@ -12,7 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +using System; using SeqCli.Config; +using SeqCli.Csv; using SeqCli.Output; using Serilog; using Serilog.Core; @@ -31,7 +33,9 @@ public OutputFormatFeature(SeqCliOutputConfig outputConfig) _noColor = outputConfig.DisableColor; } - public static ConsoleTheme ConsoleTheme => SystemConsoleTheme.Literate; + public bool Json => _json; + + ConsoleTheme Theme => _noColor ? ConsoleTheme.None : SystemConsoleTheme.Literate; public override void Enable(OptionSet options) { @@ -54,9 +58,22 @@ public Logger CreateOutputLogger() else outputConfiguration.WriteTo.Console( outputTemplate: "[{Timestamp:o} {Level:u3}] {Message:lj} {Properties:j}{NewLine}{Exception}", - theme: _noColor ? ConsoleTheme.None : ConsoleTheme); + theme: Theme); return outputConfiguration.CreateLogger(); } + + public void WriteCsv(string csv) + { + if (_noColor) + { + Console.Write(csv); + } + else + { + var tokens = new CsvTokenizer().Tokenize(csv); + CsvWriter.WriteCsv(tokens, Theme, Console.Out, true); + } + } } } diff --git a/src/SeqCli/Program.cs b/src/SeqCli/Program.cs index 668db97c..a5289f17 100644 --- a/src/SeqCli/Program.cs +++ b/src/SeqCli/Program.cs @@ -13,6 +13,7 @@ // limitations under the License. using System; +using System.Text; using System.Threading.Tasks; using Autofac; using SeqCli.Cli; @@ -34,6 +35,10 @@ static async Task Main(string[] args) try { + Console.InputEncoding = + Console.OutputEncoding = + new UTF8Encoding(encoderShouldEmitUTF8Identifier: false); + TaskScheduler.UnobservedTaskException += (s,e) => Log.Error(e.Exception, "Unobserved task exception: {UnobservedExceptionMessage}");