Azure Functions QueueTrigger with variable name, QueueTriggerAttribute restrictions - c#

Assume the following typical Queue Trigger function:
public void Run([QueueTrigger("queue1")]object data, ILogger log)
{
// Do something with data
}
My problem is that "queue1" has to be a constant field, so it has to be defined at compile time.
Also, I'd want to have a base class for Queue Triggers, that could work like this:
public abstract class QueueBase<TModel>
{
public void Run([QueueTrigger("queueName")]TModel data, ILogger log)
{
// Do something with data, log something etc.
OnRunExecuted(data);
// Do something with data, log something etc.
}
public abstract void OnRunExecuted(TModel data);
}
with this, I could write own classes which inherit from QueueBase but can even live inside a library which doesn't have Microsoft.Azure.WebJobs dependency:
public class MyQueueHandler : QueueBase<MyModel>
{
public void OnRunExecuted(MyModel data) => ...;
}
But it's impossible to pass in a Queue name... is it?

See binding expressions:
In short, you can pass in a variable queue name as "%queue-name-variable%"
[FunctionName("QueueTrigger")]
public static void Run(
[QueueTrigger("%queue-name-variable%")]string myQueueItem,
ILogger log)
{
log.LogInformation($"C# Queue trigger function processed: {myQueueItem}");
}
Where input-queue-name is defined in your configuration like
{"queue-name-variable": "queue-name-in-current-env"}

As i remember attribute QueueTrigger accept only const string, so you can try make some tricks using environment variables like in post how to pass dynamic queue name

Related

Azure Function QueueTrigger - Put new items back on queue

I have an Azure Function based on a QueueTrigger. This gets triggered when something appears on the queue, but after I processed this item, I then want to put new items back on the queue.
Is there a way to do this directly from within the Azure Function?
[Function("Batch")]
public async Task Run([QueueTrigger("batch", Connection = "DataQueue")] string data,
FunctionContext context)
{
var model = JsonConvert.DeserializeObject<MyObject>(data);
// 1. process model
// 2. Put items back on queue?
}
You can use output bindings as the following:
[StorageAccount("MyStorageConnectionAppSetting")]
public static class QueueFunctions
{
[FunctionName("QueueOutput")]
[return: Queue("myqueue-items")]
public static string QueueOutput([HttpTrigger] dynamic input, ILogger log)
{
log.LogInformation($"C# function processed: {input.Text}");
return input.Text;
}
}

Cannot bind parameter, cause the parameter type is not supported by the binding (HttpTrigger of Azure Functions)

I have to migrate a part of a monolith to be able to run the migrated part independently, but i'm new in azure functions.
Multiple HttpTriggers contain an a unsupported parameter type. (IMultiplierService)
public static async Task<IActionResult> GetMultiplier(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "multipliers/{id:guid}")] HttpRequest req,
string id,
IMultiplierService multiplierService){ ... }
I read online and understand that the string id is a reference to the {id:guid} in the route, but i could not find online what the purpose is of such an interface given as a parameter.
(IMultiplierService is a CRUD like interface. Contains method like 'GetById' or 'GetAll'.)
Can anyone explain how to support such a custom class as parameter input for the HttpTrigger Azure Function.
If you have questions or need more information. Go ahead.
The proper way to insert the crud like interface into the azure functions is to use dependency injection. You dont need to create static functions anymore. All you need to do is register the interface and its implementation in the startup class so that the azure functions runtime inject an instance of correct implementation of your interface. Consider following example of a azure function which uses a ISqlHelper interface. I write my non static function class as follows
public class NotifyMember
{
private readonly ISqlHelper _sqlHelper;
public NotifyMember(ISqlHelper sqlHelper)
{
_sqlHelper = sqlHelper ?? throw new ArgumentNullException(nameof(sqlHelper));
}
[FunctionName(nameof(NotifyMember))]
public async Task Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "multipliers/{id:guid}")] HttpRequest req,
string id, ILogger logger)
{//Perform function work here}
}
And I register my instance of class which implements ISqlHelper in my startup class as
[assembly: FunctionsStartup(typeof(MyFunction.Startup))]
namepace MyFunction{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddTransient<ISqlHelper, SqlHelper>();
}
}
}
For more information on how to do it refer Dependency Injection in Azure Functions

Separation of the logic of tutorial from the main app logic

Here we are developing an email client software which has a specific UI that we need to teach that to the user.
There are some classes which handle the logic of the application. for example there is function like this in the one of main classes of project:
public void ComposeMessage(string username,string message)
{
MessageComposer.ComposeMessage(username, message);
}
so, in the above function we send a message to a user.
but in the another side of the application; there is a tutorial phase . so when user goes into tutorial side of the application , he can interact with application's buttons which we tell him , but we don't want to send real messages. so we have changed the function above like this:
public void ComposeMessage(string username,string message)
{
if(!Global.IsTutorial)
MessageComposer.ComposeMessage(username, message);
}
So there are many functions which we are adding if(!Global.IsTutorial) in their body. How can we avoid this situation? We don't want to change the body of the functions in the tutorial section and we don't want to add more codes and classes into our project. We want to keep changes low as much as possible.
There is a lot of ways to achieve what you want but all of them imply that, to start with, you don't use ComposeMessage directly but create interface for it and refactor callers to inject it as a dependency:
public interface IMessageComposer
{
void ComposeMessage(string username, string message);
}
public class MyApp
{
IMessageComposer messageComposer;
public MyApp(IMessageComposer messageComposer)
{
this.messageComposer = messageComposer;
}
public void Foo()
{
messageComposer.ComposeMessage(username, message);
}
}
When you don't rely on concrete implementation and inject dependencies you may change implementation of IMessageComposer to whatever you like without actually touching original MessageComposer's or MyApp's code.
For example you can create TutorialMessageComposer like this
public class TutorialMessageComposer : IMessageComposer
{
public void ComposeMessage(string username, string password)
{
Console.WriteLine("Tutorial mode!");
}
}
And RoutingMessageComposer decorator which will check current mode and call right composer
public class RoutingMessageComposer : IMessageComposer
{
IMessageComposer composer;
IMessageComposer tutorialComposer;
public RoutingMessageComposer(IMessageComposer composer, IMessageComposer tutorialComposer)
{
this.composer = composer;
this.tutorialComposer = tutorialComposer;
}
public void ComposeMessage(string username, string message)
{
if (Global.IsTutorial)
tutorialComposer.ComposeMessage(username, message);
else
composer.ComposeMessage(username, message);
}
}
After all preparations completed, you just need to stick RoutingMessageComposer into MyApp
var app = new MyApp(new RoutingMessageComposer(new MessageComposer(), new TutorialMessageComposer()));

Test Brokered Message

Now i'm working at writing unit test on azure service bus trigger function
It's highly needed to mock somehow BrokeredMessage object that pass around into function. Function declaration is given below:
public static void Run(
[ServiceBusTrigger("saas01.queue.dbmigration", AccessRights.Manage, Connection = "connection")]BrokeredMessage message)
Unfortunately, i can't find any applicable way to mock it. It hardly mocking du to this class is sealed and i can't event create wrapper around it. Do you have some ideas about it?
Thanks for helping
,
One solution is to create a wrapper around BrokeredMessage you can test, as is done here. Here's also a MSDN post to the ServiceBus team that talks about using a wrapper too.
Note that Azure Functions V2 uses the Message class, which is public and not sealed.
[FunctionName("ServiceBusFunc")]
public static void Run([ServiceBusTrigger("myqueue", AccessRights.Manage, Connection = "ServiceBus")]BrokeredMessage myQueueItem, TraceWriter log)
{
var message = new MyBrokeredMessage(myQueueItem);
BusinessLogic(message, log);
}
public static void BusinessLogic(MyBrokeredMessage myMessage, TraceWriter log)
{
var stream = myMessage.GetBody<Stream>();
var reader = new StreamReader(stream);
log.Info($"C# ServiceBus queue trigger function processed message: '{reader.ReadToEnd() }'");
}
public class MyBrokeredMessage
{
private BrokeredMessage _msg;
public MyBrokeredMessage(BrokeredMessage msg) => _msg = msg;
public T GetBody<T>()
{
return _msg.GetBody<T>();
}
}

can I pass a custom property to NLOG and output to file?

EDIT 4: "From" seems to be a reserved word in NLog. Changing it "FromID" worked. this is an awesome way to pass variables to NLog and still keep your code clean !!!! THANK MIKE!!!
EDIT 3. I really like this idea.:
Implemented a helper class as Mike suggested below:
public class NLogHelper
{
//
// Class Properties
//
private Logger m_logger;
private Dictionary<string, object> m_properties;
//
// Constructor
//
public NLogHelper(Logger logger)
{
m_logger = logger;
m_properties = new Dictionary<string, object>();
}
//
// Setting Logger properties per instancce
//
public void Set(string key, object value)
{
m_properties.Add(key, value);
}
//
// Loggers
//
public void Debug(string format, params object[] args)
{
m_logger.Debug()
.Message(format, args)
.Properties(m_properties)
.Write();
}
and in my main code, I have:
private NLogHelper m_logger;
public void Start()
{
m_logger = new NLogHelper(LogManager.GetCurrentClassLogger());
m_logger.Set("From", "QRT123"); // Class setting.
m_logger.Debug("Hello ");
}
And the target set in the config file as follows:
<target xsi:type="File"
name ="LogFile" fileName="C:\QRT\Logs\QRTLog-${shortdate}.log"
layout ="${date}|${level}|${event-properties:item=From}|${message} "/>
But the output has a BLANK in the place of the 'from' property ???
So I'm ALMOST THERE... but it does not seem to work??
EDIT 2:
I am now trying to create my own version of the NLog call:
private void Log_Debug (string Message)
{
LogEventInfo theEvent = new LogEventInfo(LogLevel.Debug, "What is this?", Message);
theEvent.Properties["EmployeeID"] = m_employeeID;
m_logger.Log(theEvent);
}
The issue is that I have to format the string for the calls (but a huge performance deal)... but this seems like a hack??
Ideally, I would declare properties in the custom layout renderer and instead of setting those properties in the configuration file, each instance of my class would have the property set... something like [ID = m_ID] for the whole class. This way whenever a NLog is called from that class, the ID property is set and NLog's custom layout renderer can use this property to output it. Am I making sense??
I'm new to NLog and have been looking at custom renderers.
Basically, my goal is to have my log statements be:
_logger.Debug ("My Name is {0}", "Ed", ID=87);
and I'd like my rendered to be something like:
layout = ${ID} ${date} ${Level} ${Message}
That's it. ${ID} can have a default value of 0. fine. But ideally, I'd like every call to have the ability to specify an ID without needing to have 3 lines everytime I want to log.
I've seen custom renderers allowing me to customize what I output but i'm not sure how I can customize the properties I pass to it without
https://github.com/NLog/NLog/wiki/Extending%20NLog shows how I can add properties but I don't know how to call them.
Also, https://github.com/NLog/NLog/wiki/Event-Context-Layout-Renderer shows how I can set custom properties but that involved the creation of a LogEventInfo object every time I want to log something.
Nlog Custom layoutrenderer shows how to customize the output.. again... not how to customize the inputs.
This is for a Console app in C# targeting .NET 4.0 using VS2013
Thanks
-Ed
Event properties (used to be called event-context) would be the built-in way to do what you want. If you are using NLog 3.2+ you can use the fluent api, which may be a bit more appealing than creating LogEventInfo objects. You can access this api by by using the namespace NLog.Fluent.
Your layout would then be defined like this:
${event-properties:item=ID} ${date} ${Level} ${Message}
Then using the fluent api, log like this:
_logger.Debug()
.Message("My name is {0}", "Ed")
.Property("ID", 87)
.Write();
Other than setting properties per event as above, the only other option would be to set properties per thread using MDC or MDLS.
NLog dosen't have a way (that I have found) of setting per-logger properties. Internally, NLog caches Logger instances by logger name, but does not guarantee that the same instance of Logger will always be returned for a given logger name. So for example if you call LogManager.GetCurrentClassLogger() in the constructor of your class, most of the time you will get back the same instance of Logger for all instances of your class. In which case, you would not be able to have separate values on your logger, per instance of your class.
Perhaps you could create a logging helper class that you can instantiate in your class. The helper class can be initialized with per-instance property values to be logged with every message. The helper class would also provide convenience methods to log messages as above, but with one line of code. Something like this:
// Example of a class that needs to use logging
public class MyClass
{
private LoggerHelper _logger;
public MyClass(int id)
{
_logger = new LoggerHelper(LogManager.GetCurrentClassLogger());
// Per-instance values
_logger.Set("ID", id);
}
public void DoStuff()
{
_logger.Debug("My name is {0}", "Ed");
}
}
// Example of a "stateful" logger
public class LoggerHelper
{
private Logger _logger;
private Dictionary<string, object> _properties;
public LoggerHelper(Logger logger)
{
_logger = logger;
_properties = new Dictionary<string, object>();
}
public void Set(string key, object value)
{
_properties.Add(key, value);
}
public void Debug(string format, params object[] args)
{
_logger.Debug()
.Message(format, args)
.Properties(_properties)
.Write();
}
}
This would work with the same layout as above.
NLog 4.5 supports structured logging using message templates:
logger.Info("Logon by {user} from {ip_address}", "Kenny", "127.0.0.1");
See also https://github.com/NLog/NLog/wiki/How-to-use-structured-logging
See also https://github.com/NLog/NLog.Extensions.Logging/wiki/NLog-properties-with-Microsoft-Extension-Logging
Use MDLC Layout Renderer
MappedDiagnosticsLogicalContext.Set("PropertyName", "PropertyValue");
MappedDiagnosticsLogicalContext.Set("PropertyName2",
"AnotherPropertyValue");
In your nlog config:
${mdlc:item=PropertyName} ${mdlc:item=PropertyName2}
https://github.com/NLog/NLog/wiki/MDLC-Layout-Renderer
I had 6 variables I wanted to send to structured logging in multiple places (so when I get a user report I can search the log database on our key id fields). I created a logging scope class that leverages the MDLC. So it should be thread safe, work with async/await code and be 3 lines of code for 6 variables everywhere used.
public class MyClassLoggingScope : IDisposable
{
private readonly List<IDisposable> context;
public MyClassLoggingScope(MyClass varSource)
{
this.context = new List<IDisposable>
{
MappedDiagnosticsLogicalContext.SetScoped("Var1", varSource.Var1)
// list all scoped logging context variables
}
}
public void Dispose()
{
foreach (IDisposable disposable in this.context)
{
disposable.Dispose();
}
}
}
Usage:
using (new MyClassLoggingScope(this))
{
// do the things that may cause logging somewhere in the stack
}
Then as flux earlier suggested, in the logging config you can use ${mdlc:item=Var1}
This is propably not the best way to do this and not even thread safe but a quick and dirty solution: You could use Environment variables:
Environment.SetEnvironmentVariable("NLogOptionID", "87");
logger.Debug("My Name id Ed");
Environment.SetEnvironmentVariable("NLogOptionID", null);
In your layout, you can use environment variables.
${environment:variable=NLogOptionID}

Categories