I am using Azure Functions with Attributes to define functionality.
public static class PostPublishTimerTrigger
{
[FunctionName("PostPublishTimerTrigger")]
public static void Run([TimerTrigger("0 */5 * * * *")]TimerInfo myTimer,
TraceWriter log,
[Queue("post-published")] ICollector<string> postPublishedQueue)
{
// Additional code here
}
}
Is there a way to pull the Schedule 0 */5 * * * * for a configuration setting, be it with Configuration Manager or Environment Variables?
Thanks!
Yes, you can do
[TimerTrigger("%schedule%")]
and then add a setting called schedule with value 0 */5 * * * *
Related
I would like to pass the azure sql input binding where my sql command is a select statement with a parameter. This does not work. It keeps telling me the 'complete' is not a parameter. What am I doing wrong or is what I'm trying to do impossible with sql input binding? When I have a simple statement like select top(10) id, status from Dispatch - it works. Can I not pass a string to the parameters?
[FunctionName("Function1")]
public async Task Run([TimerTrigger("0 */2 * * * *")]TimerInfo myTimer,
[Sql("select top(10)id, status from Dispatch where status = #critstatus; ",
CommandType = System.Data.CommandType.Text,
Parameters = "#critstatus= {'complete'}",
ConnectionStringSetting = "SqlConnectionString")]
IAsyncEnumerable dispatch, ILogger log)
or
[FunctionName("Function1")]
public async Task Run([TimerTrigger("0 */2 * * * *")]TimerInfo myTimer,
[Sql("select top(10)id, status from Dispatch where status = \''complete\'' ",
CommandType = System.Data.CommandType.Text,
ConnectionStringSetting = "SqlConnectionString")]
IAsyncEnumerable dispatch, ILogger log)
According to this MSFT Documentation, Your SQL Query is not bindable as input to the Timer trigger as we have tested in our local environment.
Supported Input Bindings for the Azure Function Timer Trigger:
To fetch the data by giving input parameters for every 2 min (Timer Trigger) in Azure Function, we can call the SQL Query String inside the Run Method Code.
Function 1.cs:
public void Run([TimerTrigger("0 */2 * * * *")]TimerInfo myTimer, ILogger log)
{
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
string connString = "Server=<yourservername>,1433;Initial Catalog=samplesqldb0808;Persist Security Info=False;User ID=<username>;Password=<password>;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;";
//Query to fetch data
string queryString = "SELECT TOP 2 Id, Status FROM [dbo].[Dispatch] WHERE Status = 'Complete';";
//Connection
using (SqlConnection connection = new SqlConnection(connString))
{
SqlCommand command = new SqlCommand(queryString, connection);
command.Parameters.AddWithValue("#Status", "Complete");
connection.Open();
SqlDataReader reader = command.ExecuteReader();
try
{
while (reader.Read())
{
log.LogInformation(String.Format("{0}, {1}", reader["Id"], reader["Status"]));
Console.WriteLine(String.Format("{0}, {1}",
reader["Id"], reader["Status"]));// etc
}
}
finally
{
// Always call Close when done reading.
reader.Close();
}
}
}
Created Sample data in the database with below SQL Query:
CREATE TABLE dbo.Dispatch (
[Id] int primary key,
[Status] nvarchar(200) not null
)
INSERT INTO Dispatch (Id, Status)
VALUES
(571, 'Pending'), (572, 'Complete'),
(573, 'InProgress'), (598, 'Complete'),
(593, 'Complete'),(581, 'Complete'),
(597, 'InProgress'), (596, 'Pending');
Result:
I could see the results in the console for both of the methods Logging and Console WriteLine:
I was not able to get the parameter to work so for now I was able to get the timer to work with the azure sql input binding.
public async Task Run([TimerTrigger("0 */2 * * * *")] TimerInfo myTimer,
[Sql("select top(10)id, status, arrivaldate from Dispatch where
status = \'in progress\';" ,
CommandType = System.Data.CommandType.Text,
ConnectionStringSetting = "SqlConnectionString")]
IAsyncEnumerable<Dispatch> dispatch, ILogger log)
{
IAsyncEnumerator<Dispatch> enumerator =
dispatch.GetAsyncEnumerator();
var dispatchList = new List<FindDispatch>();
while (await enumerator.MoveNextAsync())
{
dispatchList.Add(enumerator.Current);
}
await enumerator.DisposeAsync();
I have a requirement to trigger cron job at 9 am and 10.15 pm every day.
i.e the trigger will be as:
next today at 09:00:00
then today at 22:00:00
then today at 22:15:00
then next day at 09:00:00 and so on...
I have done it as * 0,15 9,22 * * * but it will also get triggered at 9.15 am which I don't want.
Please help me with creating this expression.
If this is not possible can anyone please suggest how to write multiple cron expressions in time triggered azure function. Here is my code:
[FunctionName("Function1")]
public void Run([TimerTrigger("* 0,15 9,22 * * *")] TimerInfo myTimer, ILogger log)
{
//my code here
}
As far as I know, you can't create such a CRON expression in a Function, I can think of what you need to create two functions. A function CRON Expression: * 0 9,22 * *, another CRON expression: * 15 22 * *.
AFAIK you can not create a cron expressions with different distance between each trigger.
You can solve your problem in multiple ways programmatically:
Create a single cron expression that represents the biggest common denominator. E.g.
in your example it would be 15 minutes
so expression would be 0 0/15 0 ? * * *
Make function intelligent enough to decide if it should skip or serve the trigger. E.g. a dumb approach would be:
public class CronFunction {
private void processEvent(String event) {
// process...
}
#FunctionName("cron")
public void cronFunc(
#TimerTrigger(schedule = "0 0 0/1 ? * SAT,SUN *") String event, ExecutionContext context) {
curr_day = ...;
curr_time = ...;
if ((curr_day == today and curr_time in ['09:00:00', '22:00:00', '22:15:00']) ||
(curr_day == tomorrow and curr_time in ['09:00:00'])) {
processEvent(event);
} else {
logger.debug("skipping trigger");
}
}
}
Create multiple triggers, all calling same implementation.
public class CronFunctions {
private void processEvent(String event) {
// process...
}
#FunctionName("cron1")
public void cronFunc1(
#TimerTrigger(schedule = "0 0 0/1 ? * SAT,SUN *") String event, ExecutionContext context) {
processEvent(event);
}
#FunctionName("cron2")
public void cronFunc2(
#TimerTrigger(schedule = "0 0/15 0 ? * MON,TUE,WED,THU,FRI *") String event,
ExecutionContext context) {
processEvent(event);
}
}
I have a timer-triggered function setup like this
public class PBARCronTrigger
{
private readonly eReserveFunctions _settings;
public PBARCronTrigger(IOptions<eReserveFunctions> settings)
{
_settings = settings.Value;
}
[FunctionName("PBARCronTrigger")]
public async Task Run([TimerTrigger("%PBARCron%")] TimerInfo myTimer, ILogger log)
{
log.LogInformation($"PBARCronTrigger function executing at: {DateTime.Now}");
using (var client = new HttpClient())
and I have the app setting for PBARCron set to every 5 minutes:
but the trigger is not triggering. I connect to live logs and nothing happens. It keeps on saying "No new trace in the past x min(s)"
Your cron expression doesn't look right to me. Checking it in an evaluator even states that it's non-standard and may not work in every environment.
I think what you want is 0-55/5 * * * *, or more simply, */5 * * * *.
We have to write multiple triggers. I was hoping to create separate functions, based on the trigger types. So if I need 5 timer triggers, that will run at different times, I would create one Timer trigger function class and name the functions like [TimerTrigger1], [TimerTrigger2], [TimerTrigger3] ... and so forth. After I added the code I am not sure if I can do that anymore.
Can someone suggest how I can go about adding multiple triggers? I can't have two Run functions under one class.
public static class TimerTrigger
{
[FunctionName("InsertTimerTrigger1")]
public static void Run([TimerTrigger("0 */5 * * * *")]TimerInfo myTimer, ILogger log)
{
// Do task 1
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
}
[FunctionName("InsertTimerTrigger2")]
public static void Run([TimerTrigger("0 */15 * * * *")]TimerInfo myTimer, ILogger log)
{
//Do Task 2
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
}
}
You can create multiple functions in Single Class. You can change Run Method name.
public static class Function1
{
[FunctionName("Function1")]
public static void Method1([TimerTrigger("0 */2 * * * *")]TimerInfo myTimer, TraceWriter log)
{
log.Info($"C# Timer trigger function executed at: {DateTime.Now}");
}
[FunctionName("Function2")]
public static void Method2([TimerTrigger("0 */3 * * * *")]TimerInfo myTimer, TraceWriter log)
{
log.Info($"C# Timer trigger function executed at: {DateTime.Now}");
}
}
But I will recommend, Create multiple functions will help you (5 in your case).
If you are using common business logic, you can put in a common class and inject in all function.
You can independently Enable/Disable/Delete function from FunctionApp Instance.
You can monitor each function independently (from Function Monitor section)
You can choose any name for the methods. (Naming it as "Run" is not a requirement.)
public static class TimerTrigger
{
[FunctionName("InsertTimerTrigger1")]
public static void InsertTimerTrigger1([TimerTrigger("0 */5 * * * *")]TimerInfo myTimer, ILogger log)
{
// Do task 1
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
}
[FunctionName("InsertTimerTrigger2")]
public static void InsertTimerTrigger2([TimerTrigger("0 */15 * * * *")]TimerInfo myTimer, ILogger log)
{
//Do Task 2
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
}
}
I like to intercept some (the most important) log messages and display them inside the GUI or check if any errors where logged. Normally I use java.util.logging and here the java.util.logging.Handler with and java.util.logging.Formatter.
However the current project uses Log4J (2.x) and there every feature has different names and seems to be at least four times as complex.
Can anybody give me some hints on how to archive something like this with Log4J:
/**
* <p>
* logs error so they can be returned to the server (and tested in unit tests)
* </p>
*
* #author "Martin Krischik" <martin.krischik#noser.com>
* #version 1.0 $Revision: 2229 $
* #see java.util.logging.Handler
* #since 1.0
*/
#SuppressWarnings ("synthetic-access")
protected final class LogHandler
extends
java.util.logging.Handler
{
/**
* <p>
* A very simple formatter which displays only what is relevant to end users. Developer
* should look at the log file
* </p>
*
* #author "Martin Krischik" <martin.krischik#noser.com>
* #version 1.0 $Revision: 2229 $
* #see java.util.logging.Formatter
* #since 1.0
*/
private class LogFormatter
extends
java.util.logging.Formatter
{
/**
* <p>
* line separator
* </p>
*/
private final String lineSeparator = System.getProperty ("line.separator");
/**
* <p>
* Format the given LogRecord.
* </p>
*
* #param record
* the log record to be formatted.
* #return a formatted log record
* #see java.util.logging.Formatter#format(java.util.logging.LogRecord)
*/
#Override
public synchronized String format (final java.util.logging.LogRecord record)
{
final StringBuilder retval = new StringBuilder (4096);
final String message = this.formatMessage (record);
final java.util.logging.Level level = record.getLevel ();
retval.append (level.getLocalizedName ());
retval.append (": ");
retval.append (message);
retval.append (this.lineSeparator);
return retval.toString ();
} // format
} // LogFormatter
/**
* <p>
* A very simple formatter which displays only what is relevant to end users. Developer
* should look at the log file
* </p>
*/
private final DBUpdate.LogHandler.LogFormatter formatter =
new DBUpdate.LogHandler.LogFormatter ();
/**
* #throws SecurityException
* some severity error
* #see java.util.logging.Handler#close()
*/
#Override
public void close ()
throws SecurityException
{
return;
} // close
/**
* #see java.util.logging.Handler#flush()
*/
#Override
public void flush ()
{
return;
} // flush
/**
* #param record
* record to log.
* #see java.util.logging.Handler#publish(java.util.logging.LogRecord)
*/
#Override
public void publish (final java.util.logging.LogRecord record)
{
if (record.getLevel ().intValue () >= this.getLevel ().intValue ())
{
REST_Responce.this.errorMessages.add (this.formatter.format (record));
} // if
return;
} // publish
} // LogHandler
You could try to subclass AbstractAppender.
Appender maps to Handler
Layout maps to Formatter
ErrorHandler maps to ErrorManager