How can I get structured logging when using e.g. Serilog with Servicestack?
The examples from both Serilog and NLog have the form Log.Information("Hello World from {FirstName}", "Thomas"); for which I cannot find a matching method signature in ServiceStack.
See logging docs for enhanced Serilog Logging APIs whilst this existing StackOverflow answer shows a Serilog Enrichers example.
SerilogLoggerTests.cs shows different examples of using Serilog:
var log = new SerilogLogger(GetType());
const string message = "Error Message";
const string messageFormat = "Message Format: message: {0}, exception: {1}";
var ex = new Exception();
log.Info(message);
log.Info(message, ex);
log.InfoFormat(messageFormat, message, ex.Message);
log.Info(ex, messageFormat, messageFormat, ex);
and a Log Context example with custom properties:
var log = new SerilogLogger(new LoggerConfiguration()
.WriteTo.Sink(sink).CreateLogger());
var messageTemplate = "Testing adding {prop2} props";
log.ForContext("prop", "value").InfoFormat(messageTemplate, "awesome");
There's also PushProperty() APIs to assign custom properties, see Serilog's Enrichment docs for examples.
Related
I'm currently using an .NET 6.0 isolated function with an HTTP trigger.
Because the activity is not being filled, I've added a middleware that creates the activity based on the W3C traceparent header.
My expectation based on MS docs is that when using Application Insights, the operation_ParentId would relate directly to the ParentSpanId of an Activity where logging is done.
Yet that is not what I'm seeing. What I'm seeing is following
Application A sends a request using traceparent = 00-3abe9f15e940badc5f1521e6eb1eb411-bfd30439c918c783-00
In middleware of application B, an activity is started and a message is logged. I can also validate that the ParentId of the activity is equal to 00-3abe9f15e940badc5f1521e6eb1eb411-bfd30439c918c783-00. The ParentSpanId is equal to bfd30439c918c783
using var requestActivity = new Activity(context.FunctionDefinition.Name);
requestActivity.SetParentId(traceParent);
requestActivity.Start();
_logger.LogInformation("Invoking '{Name}'", context.FunctionDefinition.Name);
In application insights I see OperationId being equal to the WC3 trace-id 3abe9f15e940badc5f1521e6eb1eb411 as expected. However, the operation_ParentId is a span I've not seen before. It is neither the requestActivity.SpanId nor the requestActivity.ParentSpanId.
What is happening that I do not understand? Does Application Insights not use the active Activity when logging?
my app configuration
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults(worker =>
{
worker.UseMiddleware<TracingMiddleware>();
})
.ConfigureServices(collection =>
{
})
.ConfigureLogging(x=>x.AddApplicationInsights())
.Build();
host.Run();
My middleware function
public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next)
{
using var requestActivity = new Activity(context.FunctionDefinition.Name);
SetParentId(context, requestActivity);
requestActivity.Start();
_logger.LogInformation("Invoking '{Name}'", context.FunctionDefinition.Name);
var t = Activity.Current;
System.Console.WriteLine();
System.Console.WriteLine($"Activity.TraceId: {t.TraceId}");
System.Console.WriteLine($"Activity.ParentId: {t.ParentId}");
System.Console.WriteLine($"Activity.SpanId: {t.SpanId}");
await next(context);
var statusCode = (context.Items != null)
? context.GetHttpResponseData()?.StatusCode
: System.Net.HttpStatusCode.OK;
_logger.LogInformation(
"Executed '{Name}', Result {Result}, Duration = {Duration}ms",
context.FunctionDefinition.Name,
statusCode,
(DateTime.UtcNow - requestActivity.StartTimeUtc).TotalMilliseconds);
requestActivity.Stop();
}
As per the MS-Doc the Operation ID is equal to TraceId. And the Operation_parent Id has the combination of both Trace-ID with Parent-ID. The Format has .
The operation_ParentId field is in the format
<trace-id>.<parent-id>, where both trace-id and parent-id are
taken from the trace header that was passed in the request.
I have noticed that It was not actually same. I have tired with many possible ways. I end up with The operation-ID is not equal to the Trace-ID and the Operation-parent-ID is not equal to < Trace-ID>.< Parent-ID>.
In my middle ware i have added the current context like below
ILogger logger = context.GetLogger<MyCustomMiddleware>();
//logger.LogInformation("From function: {message}", message);
using var requestActivity = new Activity(context.FunctionDefinition.Name);
//t.SetParentId(context, requestActivity);
requestActivity.Start();
logger.LogInformation("Invoking '{Name}'", context.FunctionDefinition.Name);
//requestActivity.SetParentId(Convert.ToString(requestActivity.RootId));
requestActivity.SetParentId(requestActivity.TraceId,requestActivity.SpanId,ActivityTraceFlags.None);
var t = Activity.Current;
//Activity.SetParentId(context, requestActivity,);
//t.SetParentId(context, requestActivity)
logger.LogInformation($"Activity.TraceId: {t.TraceId}");
logger.LogInformation($"Activity.ParentId: {t.ParentId}");
logger.LogInformation($"Activity.SpanId: {t.SpanId}");
logger.LogInformation($"Activity.Id: {t.Id}");
logger.LogInformation($"Activity.ParentSpanId: {t.ParentSpanId}");
logger.LogInformation($"Activity.RootId: {t.RootId}");
logger.LogInformation($"Activity.Parent: {t.Parent}");
Result I got.
Refer here for one of my workarounds with more details.
I am using .net core web app as the publisher and .net core console app as subscriber.
I am able to successfully pass messages between these two systems using Managed Identities - set up in Azure portal.
My question is I need to add metadata to the the message that is being sent. How do I do that ?
Below is my publisher code :
string data = JsonConvert.SerializeObject(payloadEvents);
Message message = new Message(Encoding.UTF8.GetBytes(data));
var tokenProvider = TokenProvider.CreateManagedIdentityTokenProvider();
TopicClient sendClient = new TopicClient(_serviceBusNamespace, _topicName, tokenProvider, retryPolicy: null);
await sendClient.SendAsync(message);
Message object has a property called UserProperties that can be used to set custom metadata for that message.
Something like:
string data = JsonConvert.SerializeObject(payloadEvents);
Message message = new Message(Encoding.UTF8.GetBytes(data));
message.UserProperties.Add("key1", "value1");
message.UserProperties.Add("key2", "value2");
var tokenProvider = TokenProvider.CreateManagedIdentityTokenProvider();
TopicClient sendClient = new TopicClient(_serviceBusNamespace, _topicName, tokenProvider, retryPolicy: null);
await sendClient.SendAsync(message);
I'm new to azure service bus and masstransit. I'm looking for a solution to a specific situation.
I have a azure service bus topic with multiple subscribers. Subscriber will receive message based on filters. I've created the topic and subscriber with code below
class Program
{
static void Main(string[] args)
{
string connectionString = "Endpoint connection string";
// the names of topics and subscriptions we'll be working with
const string topicName = "MyTestTopic";
const string allMessagesSubName = "AllMessages";
const string filteredSubName1 = "Filtered1";
const string filteredSubName2 = "Filtered2";
// let's create the topic if it doesn't already exist...
var namespaceManager = NamespaceManager.CreateFromConnectionString(connectionString);
if (!namespaceManager.TopicExists(topicName))
{
var td = new TopicDescription(topicName);
namespaceManager.CreateTopic(td.Path);
}
if (!namespaceManager.SubscriptionExists(topicName, allMessagesSubName))
{
namespaceManager.CreateSubscription(topicName, allMessagesSubName);
}
if (!namespaceManager.SubscriptionExists(topicName, filteredSubName1))
{
namespaceManager.CreateSubscription(
new SubscriptionDescription(topicName, filteredSubName1),
new Microsoft.ServiceBus.Messaging.SqlFilter("From LIKE '%Smith'"));
}
if (!namespaceManager.SubscriptionExists(topicName, filteredSubName2))
{
namespaceManager.CreateSubscription(
new SubscriptionDescription(topicName, filteredSubName2),
new Microsoft.ServiceBus.Messaging.SqlFilter("sys.Label='important'"));
}
var message1 = new BrokeredMessage("Hello World");
var message2 = new BrokeredMessage("Second message");
message2.Label = "important";
var message3 = new BrokeredMessage("Third message");
message3.Properties["From"] = "Kelly Smith";
message3.Label = "information";
var client = TopicClient.CreateFromConnectionString(connectionString, topicName);
client.Send(message1);
client.Send(message2);
client.Send(message3);
client.Close();
}
}
Here in the code we're adding Message custom properties like From.
Now I want to send such message using masstransit. In masstransit I cannot find any option of adding Message custom properties using the Publish() method. Is there any way that I can send these messages using masstransit where these filters can be used?
NB: I've read the answer of this question But the anwer here tells us to filter the messages in subscriber side. What I want is that this filtering will occur before reaching the subscriber.
When using Azure Service Bus with MassTransit, you can add subscription endpoints in additional to regular endpoints. When configuring a subscription endpoint, you should be able to specify rules and/or filters as part of the subscription. Which is exactly what you're doing above, so that is handled.
The other part, adding properties to the message, can be done by adding text headers to the SendContext. Those headers are copied to the message Properties collection, which I believe can be used to filter messages using a "SQL" filter (which is configured on the subscription endpoint, or the topic subscription on a receive endpoint).
I have a console application logging to TrackRequest in a TelemetryClient, but I can't figure out what tile to display in the Azure portal to see the data.
Is there some guidance that lists each method, the data, and what tile exposes it?
With the following code in a console app:
TelemetryClient client = new TelemetryClient(new TelemetryConfiguration()
{
InstrumentationKey = "<your ikey>",
TelemetryChannel = new InMemoryChannel()
});
client.TrackRequest("Hello", DateTimeOffset.Now, TimeSpan.FromSeconds(1), "200", true);
Console.ReadLine();
Request data will appear on main overview page and in search results.
I have an Azure Reporting Services instance I want to connect to via the Report Execution Web Service. I have referenced this article to connect. However, I am receiving an error...
The URL of the service is:
i593ehr-i.reporting.windows.net
I connected to:
i593ehr-i.reporting.windows.net/ReportServer/ReportExecution2005.asmx
and downloaded the WSDL file. It should be noted that the documentation used ReportExecution2010.asmx, but that didn't direct to a WSDL file... I used the command supplied in the file to generate a proxy class. I then used this code to connect:
var service = new ReportExecutionService();
service.CookieContainer = new CookieContainer();
service.Credentials = new NetworkCredential("report", "******", "i593ehr-i.reporting.windows.net");
service.LoadReport2(reportPath, null);
string extension;
string mimeType;
string encoding;
Warning[] warnings;
string[] streamIds;
var reportData = service.Render("PDF", null, out extension, out mimeType, out encoding, out warnings, out streamIds);
File.WriteAllBytes(outputFile, reportData);
and it's returning the message:
The Authentication Extension threw an unexpected exception or returned a value that is not valid: identity==null. (rsAuthenticationExtensionError)
What am I doing wrong?
It turns out that I needed to use the LogonUser method instead of NetworkCredentials, which the documentation specified but I must have overlooked... The code should be:
service.LogonUser("report", "******", "i593ehr-i.reporting.windows.net");