diff --git a/docs/README.md b/docs/README.md index 69ce9c74..57a036d2 100644 --- a/docs/README.md +++ b/docs/README.md @@ -16,7 +16,8 @@ |-------|------------| |**[Using Ingress](recipes/ingress.md)** | Using `tye` with an ingress for serving public traffic. |**[Using Distributed Tracing](recipes/distributed_tracing.md)** | Using zipkin for distributed tracing. -|**[Logging with Elastic Stack](recipes/logging.md)** | Using Elastic Stack for logging. +|**[Logging with Elastic Stack](recipes/logging_elastic.md)** | Using Elastic Stack for logging. +|**[Logging with Seq](recipes/logging_seq.md)** | Using Seq for logging. |**[Using Dapr with Tye](recipes/dapr.md)** | Using `tye` for local development and deployment with a [Dapr](https://dapr.io) application. diff --git a/docs/recipes/logging.md b/docs/recipes/logging_elastic.md similarity index 100% rename from docs/recipes/logging.md rename to docs/recipes/logging_elastic.md diff --git a/docs/recipes/logging_seq.md b/docs/recipes/logging_seq.md new file mode 100644 index 00000000..93ec0ac7 --- /dev/null +++ b/docs/recipes/logging_seq.md @@ -0,0 +1,50 @@ +# Logging with Seq + +Seq is a popular log-aggregation system that gives you a powerful log search and dashboard engine with views across all of your services. + +Tye can push logs to Seq easily without the need for any SDKs or code changes in your services. + +## Getting started: running locally with Seq + +> :bulb: If you want an existing sample to run, the [sample here](https://github.com/dotnet/tye/tree/master/samples/frontend-backend) will do. This recipe will show examples of UI and data based on that sample. You own application will work fine, but the data and examples will look different. + +The first step is to add the `seq` extension to your `tye.yaml`. Add the `extensions` node and its children from the example below. + +```yaml +name: frontend-backend + +extensions: +- name: seq + logPath: ./.logs + +services: +- name: backend + project: backend/backend.csproj +- name: frontend + project: frontend/frontend.csproj +``` + +The `logPath` property here configures the path where Seq will store its data. + +Now launch the application with Tye: + +```sh +tye run +``` + +If you navigate to the Tye dashboard you should see an extra service (`seq`) in the list of services. + + +image + +Visit the first URI (`http://localhost:5341`) in your browser to access the Seq dashboard. + +Now you're ready to view the data! After it loads, it should look like the screenshot below: + +image + +Now you can see the logs from your application with each field broken out into structured format. If you take advantage of [structured logging](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-3.1#log-message-template) then you'll see your own data included in structured form here alongside framework logs. + +It should like the screenshot below: + +image diff --git a/src/Microsoft.Tye.Extensions/Seq/SeqExtensions.cs b/src/Microsoft.Tye.Extensions/Seq/SeqExtensions.cs new file mode 100644 index 00000000..d259c98c --- /dev/null +++ b/src/Microsoft.Tye.Extensions/Seq/SeqExtensions.cs @@ -0,0 +1,88 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.Tye.Extensions.Seq +{ + internal sealed class SeqExtension : Extension + { + public override Task ProcessAsync(ExtensionContext context, ExtensionConfiguration config) + { + if (context.Application.Services.Any(s => s.Name == "seq")) + { + context.Output.WriteDebugLine("seq service already configured. Skipping..."); + } + else + { + context.Output.WriteDebugLine("Injecting seq service..."); + + var seq = new ContainerServiceBuilder("seq", "datalust/seq") + { + EnvironmentVariables = + { + new EnvironmentVariableBuilder("ACCEPT_EULA") + { + Value = "Y" + }, + }, + Bindings = + { + new BindingBuilder() + { + Port = 5341, + ContainerPort = 80, + Protocol = "http", + }, + }, + }; + context.Application.Services.Add(seq); + + if (config.Data.TryGetValue("logPath", out var obj) && + obj is string logPath && + !string.IsNullOrEmpty(logPath)) + { + seq.Volumes.Add(new VolumeBuilder(logPath, "seq-data", "/data")); + } + + foreach (var s in context.Application.Services) + { + if (object.ReferenceEquals(s, seq)) + { + continue; + } + + // make seq available as a dependency of everything. + if (!s.Dependencies.Contains(seq.Name)) + { + s.Dependencies.Add(seq.Name); + } + } + } + + if (context.Operation == ExtensionContext.OperationKind.LocalRun) + { + if (context.Options!.LoggingProvider is null) + { + // For local development we hardcode the port and hostname + context.Options.LoggingProvider = "seq=http://localhost:5341"; + } + } + else if (context.Operation == ExtensionContext.OperationKind.Deploy) + { + foreach (var project in context.Application.Services.OfType()) + { + var sidecar = DiagnosticAgent.GetOrAddSidecar(project); + + // Use service discovery to find seq + sidecar.Args.Add("--provider:seq=service:seq"); + sidecar.Dependencies.Add("seq"); + } + } + + return Task.CompletedTask; + } + } +} diff --git a/src/Microsoft.Tye.Extensions/WellKnownExtensions.cs b/src/Microsoft.Tye.Extensions/WellKnownExtensions.cs index 4457b179..ecbe5740 100644 --- a/src/Microsoft.Tye.Extensions/WellKnownExtensions.cs +++ b/src/Microsoft.Tye.Extensions/WellKnownExtensions.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using Microsoft.Tye.Extensions.Dapr; using Microsoft.Tye.Extensions.Elastic; +using Microsoft.Tye.Extensions.Seq; using Microsoft.Tye.Extensions.Zipkin; namespace Microsoft.Tye.Extensions @@ -16,6 +17,7 @@ namespace Microsoft.Tye.Extensions { { "dapr", new DaprExtension() }, { "elastic", new ElasticStackExtension() }, + { "seq", new SeqExtension() }, { "zipkin", new ZipkinExtension() }, }; } diff --git a/src/Microsoft.Tye.Hosting.Diagnostics/LoggingSink.cs b/src/Microsoft.Tye.Hosting.Diagnostics/LoggingSink.cs index 394e47e1..01e0de22 100644 --- a/src/Microsoft.Tye.Hosting.Diagnostics/LoggingSink.cs +++ b/src/Microsoft.Tye.Hosting.Diagnostics/LoggingSink.cs @@ -170,7 +170,7 @@ namespace Microsoft.Tye.Hosting.Diagnostics } // This is the logger factory for application logs. It allows re-routing event pipe collected logs (structured logs) - // to any of the supported sinks, currently (elastic search and app insights) + // to any of the supported sinks, currently (elastic search, console, seq, and app insights) private void ConfigureLogging(string serviceName, string replicaName, ILoggingBuilder builder) { if (string.Equals(_provider.Key, "elastic", StringComparison.OrdinalIgnoreCase) &&