Azure Webjob TextWriter logger being disposed in the middle of my method - azure-web-app-service

I'm using a Webjob with the Windows Azure Storage SDK. When a new item shows up in a Queue, a method in my class is invoked. According to the SDK docs, if I take a TextWriter as a parameter to my method, the SDK will provide me with a TextWriter that I can write to which will show up in the Webjob's logging infrastructure. This makes it pretty easy to diagnose issues and troubleshoot things.
public async static void ProcessQueueMessage([QueueTrigger("queueName")]MyModelType model, TextWriter logger)
{
await logger.WriteLineAsync(string.Format("Processing Item {0}", model.SasUrl));
// Some work here
await logger.WriteLineAsync(string.Format("Done Processing Item {0}", model.SasUrl));
}
However, very frequently, within the body of my method, the TextWriter is being disposed of. I'm getting the following exception on the 2nd logger.WriteLineAsync:
System.ObjectDisposedException was unhandled by user code
HResult=-2146232798
Message=Cannot write to a closed TextWriter.
Source=mscorlib
ObjectName=""
StackTrace:
at System.IO.__Error.WriterClosed()
at System.IO.StringWriter.Write(Char[] buffer, Int32 index, Int32 count)
at System.IO.TextWriter.WriteLine(String value)
at System.IO.TextWriter.SyncTextWriter.WriteLine(String value)
at System.IO.TextWriter.SyncTextWriter.WriteLineAsync(String value)
at NameOfProject.Program.<ProcessQueueMessage>d__8.MoveNext() in c:\Dev\Path\To\Program.cs:line 173
InnerException:
I can't find others having this problem, so I can't imagine there is a bug in the SDK or webjobs infrastructure.
Is there a way to tell if the logger is disposed of ahead of the call?
Is there a way to create a new logger within my method that will participate in the WebJobs logging subsystems and UI?

That's because your method returns void. Try returning Task instead

Related

Azure Function CancellationToken

I'm quite new to AzureFunctions so excuse me for maybe a simple question...
I have got the following code which is fired when I have an item on the queue
[FunctionName("ProcessCompanies")]
public async Task ProcessCompaniesAsync([ActivityTrigger] IDurableOrchestrationContext context,
[Queue("outqueue"), StorageAccount("AzureWebJobsStorage")] ICollector<myCompanyData> msg,
ILogger log)
{
log.LogInformation("Getting companies");
var companies = await _myService.GetCompaniesAsync(); //here how to pass cancellation token?
log.LogInformation($"Found {companies.companies.Count} companies.");
companies.companies.ForEach(msg.Add);
log.LogInformation($"{companies.companies.Count} companies added to queue.");
}
How can I pass to the Async call the CancellationToken? and mainly, should I pass it? in this case I perform an HTTP API request
You can inject a CancellationToken into the function method. It is a way to get notified about a graceful shutdown of the function. See https://learn.microsoft.com/en-us/azure/azure-functions/functions-dotnet-class-library?tabs=v4%2Ccmd#cancellation-tokens
Once injected you can pass it to downstream calls. Whether you should pass it to that API is up to you. There is some short period of time before the process killed so if the call does not take long you could just try to finish it.

Cannot bind parameter 'lockToken' in Azure function app in .net framework?

I was using .net core Azure function in an app, with no problem faced, with the below code.
But when I create an Azure function app using .net framework it is throwing below error.
I tried all possible google solutions. the error is clearly because of nuget and library as same code works fine for v2 .net core function app.
[FunctionName("Function1")]
public static async System.Threading.Tasks.Task RunAsync([ServiceBusTrigger("topic", "sub", Connection = "ConnectionStringSettingName")]Message mySbMsg, string lockToken, MessageReceiver messageReceiver, ILogger log)
{
log.LogInformation($"C# ServiceBus topic trigger function processed message: {mySbMsg}");
}
I even tried this as well:
[FunctionName("Function1")]
public static async System.Threading.Tasks.Task RunAsync([ServiceBusTrigger("topic", "sub", Connection = "ConnectionStringSettingName")]Message mySbMsg, Guid lockToken, MessageReceiver messageReceiver, ILogger log)
{
log.LogInformation($"C# ServiceBus topic trigger function processed message: {mySbMsg}");
}
If I took it as guid or string or vice versa, it gives the error:
Error indexing method 'Function1.RunAsync'.
Microsoft.Azure.WebJobs.Host: Cannot bind parameter 'lockToken' to
type Guid. Make sure the parameter Type is supported by the binding.
If you're using binding extensions (e.g. ServiceBus, Timers, etc.)
make sure you've called the registration method for the extension(s)
in your startup code (e.g. config.UseServiceBus(), config.UseTimers(),
etc.).
Vice versa error:
RunAsync: Microsoft.Azure.WebJobs.Host: Error indexing method
'Function1.RunAsync'. Microsoft.Azure.WebJobs.Host: Cannot bind
parameter 'lockToken' to type Guid. Make sure the parameter Type is
supported by the binding. If you're using binding extensions (e.g.
ServiceBus, Timers, etc.) make sure you've called the registration
method for the extension(s) in your startup code (e.g.
config.UseServiceBus(), config.UseTimers(), etc.).
The locktoken parameter that you are sending in is a string.
The error message is that it cannot bind the locktoken to a Guid.
Therefore, it is probably expecting a string that is a valid Guid, and you are sending in something else.

Handling errors/failures occurred in Azure Durable Functions called by queue-triggered Azure Functions

We have an Azure Storage Queue which triggers an azure function once a payload/message hits the queue. The queue-triggered function invokes another durable function to process the message/payload.
Here it is the code snippet:
[FunctionName("QueueTriggerFunction")]
public Task QueueTriggerFunction(
[QueueTrigger("MyQueue", Connection = "MyStorage")]string item,
[OrchestrationClient] DurableOrchestrationClient client,
ILogger log)
=> client.StartNewAsync("Processor", JsonConvert.DeserializeObject<MyObject>(item));
And the durable function looks like the following code sample:
[FunctionName("Processor")]
public async Task ConcurrencyProcessorAsync(
[OrchestrationTrigger] DurableOrchestrationContext context,
ILogger log)
{
var myObject= context.GetInput<MyObject>();
if(ObjectProcessor(myObject) == false)
{
throw new Exception("Processor failed");
}
}
I'd like the payload to end up in the poison messages queue if the exception above is raised upon failing the ObjectProcessor method but it's not happening in reality because the exception does not bublle up through the orchestrator client. Any suggestions on how to make this exception thrown back to the caller function which is a queue-triggered one to make the payload appear in the poison messages queue?
You can't.
The QueueTriggerFunction just starts the Orchestration. After that it's life cycle ends.
I believe you can directly add your payload to poison queue using either Azure Storage Services REST API or this .Net library
Please note that name of poison queue == $"{queueName}-poison"

Azure function 1.0 Logging

I am using azure function with ILogger to log exception and tracing in Application Insights. I use log.LogError with an exception object as a second parameter. However, whatever log it only comes under traces in app insights and doesn't logs the entire exception object. Is there a way to get exception object with the entire stack?
Also, the dependency is empty and I make multiple HTTP calls and I am expecting it to log all HTTP calls as dependency.
The LogError extension method takes the exception object as the first parameter. Give that a try and your Exception object should show up correctly in App Insights.
public static void LogError (this Microsoft.Extensions.Logging.ILogger logger, Exception exception, string message, params object[] args);

Call WCF service from a background thread

I’m working on a piece of code which would take in an API request to a WebAPI controller and through that invoke a WCF web service. This would block until the WCF service responded and cause issues such as timeouts and performance problems. I can't use async/await for this particular use case for a number of reasons beyond my control.
I’m looking at kicking this WCF call off on a separate thread, so in the WebAPI controller I do something like:
New Thread(()=>{
//Call WCF service here
//Do something with the response
}).Start();
However, the code is blowing up. The line that calls the WCF service was moved into the above codeblock unchanged, but now I’m getting:
Cannot access a disposed object. Object name:
'System.ServiceModel.Channels.ServiceChannel'.
Looking at the stack trace when the exception is thrown, I can see the server stack trace as follows:
Server stack trace: at
System.ServiceModel.Channels.CommunicationObject.ThrowIfDisposedOrImmutable()
at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan
timeout) at
System.ServiceModel.Channels.ServiceChannel.CallOpenOnce.System.ServiceModel.Channels.ServiceChannel.ICallOnce.Call(ServiceChannel
channel, TimeSpan timeout) at
System.ServiceModel.Channels.ServiceChannel.CallOnceManager.CallOnce(TimeSpan
timeout, CallOnceManager cascade) at
System.ServiceModel.Channels.ServiceChannel.Call(String action,
Boolean oneway, ProxyOperationRuntime operation, Object[] ins,
Object[] outs, TimeSpan timeout) at
System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage
methodCall, ProxyOperationRuntime operation) at
System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage
message)
Im not hugely experienced with WCF so wondered if there was some quirk regarding calling this service in a background thread or if there was something else I needed to do instead?
I’ve tried Googling this but all the results are pertaining to calling a background thread from within a WCF service, not calling a WCF service from a background.
Any ideas?
I am pretty much sure, that you creating instance of WCF service outside thread. So previously it looked like:
using(var client = new WcfServiceClient())
{
client.CallSomeMethod();
}
And you changed it to something like:
using(var client = new WcfServiceClient())
{
new Thread(() => {
client.CallSomeMethod();
}).Start();
}
And what you need to is to move client creation into Thread:
new Thread(() => {
using(var client = new WcfServiceClient())
{
client.CallSomeMethod();
}
}).Start();
}
So I figured this out in the end. It was a Unity problem. For some reason, whereas the existing code was:
container.RegisterType<IMyServiceWrapper, MyServiceImplementation>()
I had to explicitly tell Unity how to resolve the constructor parameter:
container.RegisterType<IMyServiceWrapper, MyServiceImplementation>(
new InjectionConstructor(container.Resolve<IMyDependentService>()));
Unfortunately, I have no idea why this was required or why it fixed it?

Resources