I have configured function apps in Azure to persist log entries from the function app insights logs to an event hub which then also persists these log entries to ELK. I recently noticed that some log entries were missing and I followed this example to create a console app that would replay the log events. The console app I created based on the examples works and no exceptions are thrown, however I never find the log entries when querying ELK, they don't appear to be persisted there the way I would expect. Below is some example code of what my console app is doing.
private const string connectionString = "CONNECTION_STRING";
private const string eventHubName = "EVENT_HUB_NAME";
static EventHubBufferedProducerClient producerClient;
static async Task Main()
{
string eventDataString = "{"message": "Here's a log entry"}";*/
var eventData = JsonConvert.DeserializeObject<EventData>(eventDataString);
eventData.MessageId = "MessageIDLog1";
eventData.ContentType = "application/json";
eventData.EventBody = new BinaryData(Encoding.UTF8.GetBytes("{message": "Here's a log entry}"));
producerClient = new EventHubBufferedProducerClient(connectionString, eventHubName);
producerClient.SendEventBatchFailedAsync += args =>
{
Console.WriteLine($"Publishing failed for { args.EventBatch.Count } events. Error: { args.Exception.Message }");
return Task.CompletedTask;
};
producerClient.SendEventBatchSucceededAsync += args =>
{
Console.WriteLine($"{ args.EventBatch.Count } events were published to partition: { args.PartitionId }.");
return Task.CompletedTask;
};
try
{
await producerClient.EnqueueEventAsync(eventData);
}
finally
{
await producerClient.DisposeAsync();
}
}
I'm hoping someone out there has some experience with this and could point me in the right direction as I've been on this for a while and I'm not sure what I'm doing incorrectly.
Related
I need to send QnA users questions and answers to my Azure bot insights using telemetry. Already tried this tutorial :
https://learn.microsoft.com/en-us/azure/bot-service/bot-builder-telemetry?view=azure-bot-service-4.0&tabs=javascript
And this SO posts:
How to get the Qna Maker "Q" from Analytics Application Insights?
How can I save some custom qna maker data in azure app insights?
Thing is, first it's done for LUIS and gives no additional info to Insights, also nothing for QnA...second ones are written for C#...
I need to send question and answer to customEvents logs on Azure insights using NodeJS but I can't find how, any help ?
Thanks in advance.
///// EDIT:
Here's what I got so far (only posted the code related to the telemetry and QnA that's already working fine):
Index.js
const { ApplicationInsightsTelemetryClient, TelemetryInitializerMiddleware } = require('botbuilder-applicationinsights');
const { TelemetryLoggerMiddleware } = require('botbuilder-core');
function getTelemetryClient(instrumentationKey) {
if (instrumentationKey) {
return new ApplicationInsightsTelemetryClient(instrumentationKey);
}
return new NullTelemetryClient();
}
const server = restify.createServer();
server.use(restify.plugins.bodyParser());
var telemetryClient = getTelemetryClient(process.env.InstrumentationKey);
var telemetryLoggerMiddleware = new TelemetryLoggerMiddleware(telemetryClient);
var initializerMiddleware = new TelemetryInitializerMiddleware(telemetryLoggerMiddleware);
adapter.use(initializerMiddleware);
const mybot = new MYBOT(conversationState,userState, telemetryClient);
mybot.js
class MYBOT extends ActivityHandler {
constructor(conversationState,userState,telemetryClient) {
super();
this.conversationState = conversationState;
this.userState = userState;
this.telemetryClient = telemetryClient;
}
}
//This is how I get my qna result:
console.log(this.telemetryClient);
var result = await this.qnaMaker.getAnswers(context);
As You can see, I pass the telemetryClient to the bot file, and if I console log that item I get the complete telemetry object, but how I pass it the user question and answer so its save on insights customevents ??
Found a way to it, in case people that's looking for one of the possible solutions for Node may need it :
Basically, We use the same telemetry code process described in oficial documentation for instancing telemetry on index.js :
const { ApplicationInsightsTelemetryClient, TelemetryInitializerMiddleware } = require('botbuilder-applicationinsights');
const { TelemetryLoggerMiddleware } = require('botbuilder-core');
function getTelemetryClient(instrumentationKey) {
if (instrumentationKey) {
return new ApplicationInsightsTelemetryClient(instrumentationKey);
}
return new NullTelemetryClient();
}
const server = restify.createServer();
server.use(restify.plugins.bodyParser());
var telemetryClient = getTelemetryClient(process.env.InstrumentationKey);
var telemetryLoggerMiddleware = new TelemetryLoggerMiddleware(telemetryClient);
var initializerMiddleware = new TelemetryInitializerMiddleware(telemetryLoggerMiddleware);
adapter.use(initializerMiddleware);
const mybot = new MYBOT(conversationState,userState, telemetryClient);
Then, we pass it to the bot file (bot.js or the one you´re using):
class MYBOT extends ActivityHandler {
constructor(conversationState,userState,telemetryClient) {
super();
this.conversationState = conversationState;
this.userState = userState;
this.telemetryClient = telemetryClient;
}
}
And later in code, You can use telemetry.trackEvent method (Official docs are only in C#), but basically, it allows you to create a custom event you want to track in specifics events in your code, like when You're bot has an error or doesn´t found an answer to user. Code according to previous lines would be like this:
this.telemetryClient.trackEvent(
{name: "myEvent",
properties: {my_user_question: 'Context activity text here or your captured question',
my_bot_answer: 'bot reply or whatever'}
}
); // name and properties are part of the sintaxys, values inside properties object as you may need.
That way, on Azure insights customEvents model You will see records captured with the event name you used, also, with the properties objects as a dict in customdimensions field.
I'm looking to have our distributed event logging have proper correlation. For our Web Applications this seems to be automatic. Example of correlated logs from one of our App Services API:
However, for our other (non ASP, non WebApp) services were we use Log4Net and the App Insights appender our logs are not correlated. I tried following instructions here: https://learn.microsoft.com/en-us/azure/azure-monitor/app/correlation
Even after adding unique operation_Id attributes to each operation, we're not seeing log correlation (I also tried "Operation Id"). Example of none correlated log entry:
Any help on how to achieve this using log4net would be appreciated.
Cheers!
Across services correlation IDs are mostly propagated through headers. When AI is enabled for Web Application, it reads IDs/Context from the incoming headers and then updates outgoing headers with the appropriate IDs/Context. Within service, operation is tracked with Activity object and every telemetry emitted will be associated with this Activity, thus sharing necessary correlation IDs.
In case of Service Bus / Event Hub communication, the propagation is also supported in the recent versions (IDs/Context propagate as metadata).
If service is not web-based and AI automated correlation propagation is not working, you may need to manually get incoming ID information from some metadata if any exists, restore/initiate Activity, start AI operation with this Activity. When telemetry item is generated in scope of that Activity, it will get proper IDs and will be part of the overarching trace. With that, if telemetry is generated from Log4net trace that was executed in the scope of AI operation context then that telemetry should get right IDs.
Code sample to access correlation from headers:
public class ApplicationInsightsMiddleware : OwinMiddleware
{
// you may create a new TelemetryConfiguration instance, reuse one you already have
// or fetch the instance created by Application Insights SDK.
private readonly TelemetryConfiguration telemetryConfiguration = TelemetryConfiguration.CreateDefault();
private readonly TelemetryClient telemetryClient = new TelemetryClient(telemetryConfiguration);
public ApplicationInsightsMiddleware(OwinMiddleware next) : base(next) {}
public override async Task Invoke(IOwinContext context)
{
// Let's create and start RequestTelemetry.
var requestTelemetry = new RequestTelemetry
{
Name = $"{context.Request.Method} {context.Request.Uri.GetLeftPart(UriPartial.Path)}"
};
// If there is a Request-Id received from the upstream service, set the telemetry context accordingly.
if (context.Request.Headers.ContainsKey("Request-Id"))
{
var requestId = context.Request.Headers.Get("Request-Id");
// Get the operation ID from the Request-Id (if you follow the HTTP Protocol for Correlation).
requestTelemetry.Context.Operation.Id = GetOperationId(requestId);
requestTelemetry.Context.Operation.ParentId = requestId;
}
// StartOperation is a helper method that allows correlation of
// current operations with nested operations/telemetry
// and initializes start time and duration on telemetry items.
var operation = telemetryClient.StartOperation(requestTelemetry);
// Process the request.
try
{
await Next.Invoke(context);
}
catch (Exception e)
{
requestTelemetry.Success = false;
telemetryClient.TrackException(e);
throw;
}
finally
{
// Update status code and success as appropriate.
if (context.Response != null)
{
requestTelemetry.ResponseCode = context.Response.StatusCode.ToString();
requestTelemetry.Success = context.Response.StatusCode >= 200 && context.Response.StatusCode <= 299;
}
else
{
requestTelemetry.Success = false;
}
// Now it's time to stop the operation (and track telemetry).
telemetryClient.StopOperation(operation);
}
}
public static string GetOperationId(string id)
{
// Returns the root ID from the '|' to the first '.' if any.
int rootEnd = id.IndexOf('.');
if (rootEnd < 0)
rootEnd = id.Length;
int rootStart = id[0] == '|' ? 1 : 0;
return id.Substring(rootStart, rootEnd - rootStart);
}
}
Code sample for manual correlated operation tracking in isolation:
async Task BackgroundTask()
{
var operation = telemetryClient.StartOperation<DependencyTelemetry>(taskName);
operation.Telemetry.Type = "Background";
try
{
int progress = 0;
while (progress < 100)
{
// Process the task.
telemetryClient.TrackTrace($"done {progress++}%");
}
// Update status code and success as appropriate.
}
catch (Exception e)
{
telemetryClient.TrackException(e);
// Update status code and success as appropriate.
throw;
}
finally
{
telemetryClient.StopOperation(operation);
}
}
Please note that the most recent version of Application Insights SDK is switching to W3C correlation standard, so header names and expected format would be different as per W3C specification.
I'm trying to develop WebJob using SDK 3.0.x, and testing it locally. I've followed the sample in github without any success.
When running it locally everything is going ok, it also see the ProcessQueueMessage function but it doesn't pick the messages from the queue.
Program.cs
static void Main(string[] args)
{
var builder = new HostBuilder();
//builder.UseEnvironment(EnvironmentName.Development);
builder.ConfigureWebJobs(b =>
{
b.AddAzureStorageCoreServices();
b.AddAzureStorage();
});
builder.ConfigureAppConfiguration((context, config) =>
{
config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
});
builder.ConfigureLogging((context, b) =>
{
b.AddConsole();
// If the key exists in settings, use it to enable Application Insights.
string instrumentationKey = context.Configuration["APPINSIGHTS_INSTRUMENTATIONKEY"];
if (!string.IsNullOrEmpty(instrumentationKey))
{
b.AddApplicationInsights(o => o.InstrumentationKey = instrumentationKey);
}
});
builder.ConfigureServices((context, services) =>
{
//services.AddSingleton<IJobActivator, MyJobActivator>();
services.AddScoped<Functions, Functions>();
services.AddSingleton<IHostService, HostService>();
})
.UseConsoleLifetime();
var host = builder.Build();
using (host)
{
host.Run();
}
}
Functions.cs
public class Functions
{
private readonly IHostService _hostService;
public Functions(IHostService hostService)
{
_hostService = hostService;
}
// This function will get triggered/executed when a new message is written
// on an Azure Queue called queue.
public void ProcessQueueMessage([QueueTrigger("newrequests")] string dd,
//DateTimeOffset expirationTime,
//DateTimeOffset insertionTime,
//DateTimeOffset nextVisibleTime,
//string queueTrigger,
//string id,
//string popReceipt,
//int dequeueCount,
ILogger logger)
{
var newRequestItem = new RequestQueueItem();
logger.LogTrace($"New queue item received...");
//logger.LogInformation($" QueueRef = {id} - DequeueCount = {dequeueCount} - Message Content [Id = {newRequestItem.Id}, RequestDate = {newRequestItem.RequestDate}, Mobile = {newRequestItem.Mobile}, ProviderCode = {newRequestItem.ProviderCode}, ItemIDClass = {newRequestItem.MappingIDClass}]");
// TODO: Read the DatabaseConnectionString from App.config
logger.LogTrace($" Getting DB ConnectionString...");
var connectionString = ConfigurationManager.ConnectionStrings["DatabaseConnection"].ConnectionString;
// TODO: Initiation of provider service instance
logger.LogTrace($" Init IalbayanmtnclientserviceClient service instance...");
var bayanService = new AlbayanMtnWCFService.IalbayanmtnclientserviceClient();
// TODO: sending request to provider service endpoint and wait for response
logger.LogTrace($" Sending request to Service Endpoint...");
var response= bayanService.requestpaymenttransactionAsync("agentcode", "agentpassword", "accountno", int.Parse(newRequestItem.TransactionType), newRequestItem.MappingIDClass, newRequestItem.Mobile, (int)newRequestItem.Id).Result;
logger.LogTrace($"Done processing queue item");
}
}
Here is the screen shot for the output
Appreciate your help
Screen shot for queue messages 'newrequests'
enter image description here
From your snapshot, your webjob runs well on local. It didn't pick message because you don't add message in the newrequests queue.
The function only be triggered after you add the message. Or I will get the same result just like yours.
About the tutorial , your could refer to the official doc:Get started with the Azure WebJobs SDK. And make sure you set the right storage account. The below is my appsettings.json. Make sure the "Copy to output directory" property of the appSettings.json file is set to either Copy if newer or Copy always. Or it will run into exception:Storage account 'Storage' is not configured.
{
"ConnectionStrings": {
"AzureWebJobsStorage": "DefaultEndpointsProtocol=https;AccountName=mystorage;AccountKey=key;..."
}
}
Hope this could help you, if you still have other questions, please let me know.
I am creating a sample Azure Service Bus application. I created a namespace, topic and Subscription. I have written test messages to the topic and if I go to the subscription in the portal, I see that I have a new message every time I write a new one using the writer application.
But when I go to pull the message, nothing is retrieved. In troubleshooting, I changed the subscription name to an incorrect value and received an error. I changed it back and I get no output and none of the messages are removed when I look in Azure portal. I'm stuck... this seems easy, but it isn't working.
string connectionString = "Endpoint=sb://redacted for obvious reasons";
SubscriptionClient Client = SubscriptionClient.CreateFromConnectionString(connectionString, "NewOrders", "AllOrders");
// Configure the callback options.
OnMessageOptions options = new OnMessageOptions();
options.AutoComplete = false;
options.AutoRenewTimeout = TimeSpan.FromMinutes(1);
Client.OnMessage((message) =>
{
try
{
Console.WriteLine("Body: " + message.GetBody<string>());
message.Complete();
Console.ReadLine();
}
catch (Exception)
{
// Indicates a problem, unlock message in subscription.
message.Abandon();
}
}, options);
It seems that it is not the code issue. I create a demo to retrieve message from topic, it works correctly.
Before I try to retrieve message from topic, I send the message to the topic or make sure that there are messages for subscription. We could check that from the portal
Send message code demo.
private static void SendMessages()
{
topicClient = TopicClient.Create(TopicName);
List<BrokeredMessage> messageList = new List<BrokeredMessage>
{
CreateSampleMessage("1", "First message information"),
CreateSampleMessage("2", "Second message information"),
CreateSampleMessage("3", "Third message information")
};
Console.WriteLine("Sending messages to topic...");
foreach (BrokeredMessage message in messageList)
{
while (true)
{
try
{
topicClient.Send(message);
}
catch (MessagingException e)
{
if (!e.IsTransient)
{
Console.WriteLine(e.Message);
throw;
}
}
Console.WriteLine($"Message sent: Id = {message.MessageId}, Body = {message.GetBody<string>()}");
break;
}
}
topicClient.Close();
}
I also try your mentioned code, it also works correctly for me.
We also could get the demo project from the cloud Project from the template. We also could get more info about How to use Service Bus topics and subscriptions from the document.
So I tried running this Push notification sample for Xamarin.Android http://azure.microsoft.com/en-us/documentation/articles/partner-xamarin-mobile-services-android-get-started-push/ and after following instructions from the docs - I got it up and running. The insertion of items work absolutely fine however push notification refuses to work.
This is the error I get on Azure for push: Error: 400 - The supplied notification payload is invalid.
Anyone else tried running this sample on their device and tried push notifications? The error isn't doing much to help my case.
The sample is using PushSharp.
I'd appreciate any help. Thanks a bunch!
This is how I send push notification to Google Cloud Messaging from the back-end server.
public async Task<bool> SendNotification(int id, int index, string from, string text, string tag)
{
try
{
var payload = new
{
data = new
{
message = new
{
// this part can be anything you want
id,
index,
from,
text,
when = DateTime.UtcNow.ToString("s") + "Z"
}
}
};
var json = JsonConvert.SerializeObject(payload);
await _hubClient.SendGcmNativeNotificationAsync(json, tag);
return true;
}
catch (ArgumentException ex)
{
// This is expected when an APNS registration doesn't exist.
return false;
}
Then in your app Intent Service, you can parse the JSON "message":
protected override void OnMessage(Context context, Intent intent)
{
var message = intent.Extras.GetString("message");
// message is JSON payload
// { "id":"someid", "index":"1", "text":"some text","from"... }
var json = JObject.Parse(message);
var id = json["id"].ToString();
var index = json["index"].ToString();
var text = json["text"].ToString();
var from = json["from"].ToString();
var when = DateTime.Parse(json["when"].ToString());
// do whatever you want with your values here
}