NLog webservice target, slack icon based on log level - nlog

I'm new to NLog, and fighting with finding the solution to what I'm going for.
I'd like warning and up to get pushed to a slack channel with log level specific icons. I have got that working, technically. I have 2 pairs of targets/loggers that are hard coded to catch warnings only, and errors and above, and have 'warning' or 'error' icons hard coded into the layout template.
<target xsi:type="WebService"
name="slackWarningTarget"
url="https://hooks.slack.com/services/xx/xx/xx"
protocol="JsonPost"
encoding="utf-8"
>
<parameter name="text" type="System.String" layout=":warning: ${machinename} ${message}"/>
<parameter name="channel" type="System.String" layout="xx"/>
</target>
and
<logger name="*" minlevel="Warn" maxlevel="Warn" writeTo="slackWarningTarget">
Is there a way to better accomplish this? I want warning level to map to :warning:, etc.
I'd also like some info level stuff pushed there as well. To do this I created a named logger that always logs to the slack channel, but it results in duplicated messages if they are warning or above.
<logger name="noticeLogger" writeTo="slackInfoTarget" />
I'd guess there is a much more elegant way to do both of these things than i have come up with on my own.

You could do that from config with a ${when}, e.g.
layout="${when:when=${level}=='Warn':inner=\:warning\::else:${when:when=${level}== 'Error':inner=\:error\::else:todo}}"
but it get clumsy fast.
Another option is to register a custom layout renderer:
// register in the start of your program (e.g. main, app_start)
// usage ${slackIcon}
LayoutRenderer.Register("slackIcon", logEvent =>
{
if (logEvent.Level == LogLevel.Warn)
{
return ":warning:";
}
if (logEvent.Level == LogLevel.Error)
{
return ":error:";
}
return ":other:";
//etc
});
See How to write a custom layout renderer

Related

Spring Integration JMS/IBM MQ: how to send different message to different queue in parallel

I am working with a project which is using JMS listener to receive incoming message, and then route to different destination, currently the process only pick one destination among below 3 for each incoming message. so the xml configuration is written as below
<integration:router ref="jmsRouter" input-channel="jmsFilterOutput" default-output-channel="jmsRouterOutput" />
<integration:service-activator id="serviceActivator1" input-channel="input1"
ref="messageProcessService" method="callMsgProcessor1" />
<integration:service-activator id="serviceActivator2" input-channel="input2"
ref="messageProcessService" method="callMsgProcessor2" />
<integration:service-activator id="serviceActivator3" input-channel="sharedInput"
ref="messageProcessService" method="callMsgProcessor3" output-channel="reqChannel" />
among above 3 serviceActivator, the output-channel of the last one is defined as IBM mq in another xml configuration file.
now my job is to generate a different message from sharedInput, and send to a different queue in parallel
so I add a line as below
<integration:service-activator id="serviceActivator4" input-channel="sharedInput"
ref="messageProcessService" method="callMsgProcessorNew" output-channel="reqChannelNew" />
however when running JMS, the message from sharedInput only goes to callMsgProcessor3, and the populated message is sent to reqChannel only as well, and ignore my new destination. if I comment out the third service activator, sharedInput can go to callMsgProcessorNew, and route to new queue.
can anyone advise how I should configure to push the sharedInput go to two processors (callMsgProcessor3 and callMsgProcessorNew), and also sent to their corresponding output mq channel in parallel?
I googled online, seems router splitter or recipient list router can solve my problem? but still feeling confused after reading the related doc, and not sure how to configure it in my case. appreciate if someone can help provide a sample
please let me know if I need to provide more info to clarify the issue.
You can make a sharedInput as a PublishSubscribeChannel and have another service activator subscribed to it so the same message will go to both of them. After that you can make absolutely different flows and do whatever logic you need to parallel. See docs for more info: https://docs.spring.io/spring-integration/docs/current/reference/html/core.html#channel-implementations-publishsubscribechannel.
Also respective EIP determination : https://www.enterpriseintegrationpatterns.com/patterns/messaging/PublishSubscribeChannel.html
thanks for your reply, #Artem! I realized one thing, sharedInput only go to one destination is because it is one message. if I can duplicate the message, it will go to two destination. so I add recipient-list-router, and made change as below, and it worked!
<integration:recipient-list-router id="duplicateMsgRouter" input-channel="sharedInput"
timeout="1234"
ignore-send-failures="true"
apply-sequence="true">
<integration:recipient channel="channel1"/>
<integration:recipient channel="channel2"/>
</integration:recipient-list-router>
<integration:service-activator id="serviceActivator3" input-channel="channel1"
ref="messageProcessService" method="callMsgProcessor3" output-channel="reqChannel" />
<integration:service-activator id="serviceActivator4" input-channel="channel2"
ref="messageProcessService" method="callMsgProcessorNew" output-channel="reqChannelNew" />

How do I use the sqs-message-driven-channel-adapter in spring-integration-aws

EDIT: Here is a gist showing my log. It appears that there is ReceiveMessage and then a preSend on inputChannel:
https://gist.github.com/louisalexander/04e7d95835521efdd15455c98075e2ea
Apologies for being so dense, but I can't seem to figure out how to properly make use of the sqs-message-driven-channel-adapter
In my context file, I am configuring it as such:
<int-aws:sqs-message-driven-channel-adapter
id="my-message-driven-adapter" sqs="sqs" queues="some-queue-of-mine"
max-number-of-messages="5" visibility-timeout="200" wait-time-out="10"
send-timeout="2000" channel="inputChannel" />
I observe that messages are properly making it into some-queue-of-mine (by removing the above bit of code and sending messages to the queue). I then restart my server, enabling the message driven adapter and I observe that all the messages are consumed from the queue, but where did they go? :-/
My expectation was that the messages would be funneled into a DirectChannel named inputChannel:
<int:channel id="inputChannel"/>
That I have a service-activator consuming from as follows:
<int:service-activator ref="myConsumer"
method='execute' input-channel="inputChannel" output-channel="outputChannel">
<int:request-handler-advice-chain>
...
</int:request-handler-advice-chain>
</int:service-activator>
But of course, I am never seeing myConsumer get invoked. I imagine my understanding of how the MessageProducer mechanism works is inadequate. Can someone please correct my thinking by providing a trivial example of XML wiring?
According to the Logs the message is consumed by the handler.AbstractMessageHandler (AbstractMessageHandler.java:115) - ServiceActivator for [org.springframework.integration.handler.MethodInvokingMessageProcessor#493f49cd]. Although that might be a fully different story.
SqsMessageDrivenChannelAdapter can be supplied with the errorChannel to handle downstream exceptions. By default it is only logged.
The message sent from that adapter is like:
return new GenericMessage<>(message.getBody(), new SqsMessageHeaders(messageHeaders));
Where that message.getBody() is String. See QueueMessageUtils.createMessage().
So, be sure that your service-activator accepts that String as a paylaod and not any other type.

How to build grammar in Universal Windows Platform?

Coming from a WPF background, I was trying to find a counterpart of GrammarBuilder (in System.Speech.Recognition) to implement choices, repeat and priorities in a Universal Windows app development. But it appears more things are different in Windows.Media.SpeechRecognition than I expected.
It appears Grammar is called Contraint in this Windows.Media.SpeechRecognition namespace, which makes sense. But as I look for the replacement for GrammarBuilder and SpeechRecognitionEngine.LoadGrammar, the closest Constraint I can find is SpeechRecognitionListConstraint. The MSDN documentation says there are Commands and Probability.
But, how can we add choices and repeats into the Commands (string[])? Something like:
var builder = new GrammarBuilder();
builder.Append("What is the", 0, 1);
builder.Append("forecast for ");
builder.Append(dayChoices, 0, 3);
or in SRGS
<rule id="forecast">
<item repeat="0-1">What is the</item>
<item>forecast for</item>
<item repeat="1-">
<one-of >
<item>Monday</item>
<item>Tuesday</item>
<item>Wednesday</item>
<item>Thursday</item>
<item>Friday</item>
<item>Saturday</item>
<item>Sunday</item>
</one-of>
</item>
</rule>
The example code in the demo app only has string[] as commands.

How to make ThresholdFilter work in logback

I basically followed the instruction in Chapter 7 of the official logback documentation. Unfortunately, things don't seem to work properly, and I can't figure out why. So, here we are.
What I want to achieve is to log only info and above in console and all debug and above into log files. I have these lines in my logback.groovy:
appender("CONSOLE", ConsoleAppender) {
encoder(PatternLayoutEncoder) {
pattern = "%d{YYYY-MM-dd/HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
}
filter(ThresholdFilter) {
level = INFO
}
}
logger("vh.FileIO", DEBUG, ['CONSOLE','FILE-DEBUG'])
root(DEBUG, ['CONSOLE'])
I thought the ThresholdFilter would make CONSOLE only take info and above logs. However, I still get debug logs of vh.FileIO on the CONSOLE. For example, I still get something like this on the console:
2013-10-11/21:48:10.537 [main] DEBUG vh.FileIO - Combining all records into file ./output/sip_sample_data_output.csv
So, what am I missing here?
update
After playing with the configuration of logback, I find that the ThresholdFilter works as expected if I use an XML configuration file. However, I still would like to know why it's not working in the groovy configuration file.
Solution for the problem is the following: just add import of the class ThresholdProvider at the beginning of "logback.groovy":
import ch.qos.logback.classic.filter.ThresholdFilter
then it works as expected.
I tried the provided "logback.groovy" on a simple project. There's another problem: messages are written twice to the console. I think this could be fixed by fine-tuning "logger" and "root" calls.

How to set custom template for home page?

I've tried to set 1column template for home page using my local.xml file:
<cms_index_index>
<reference name="root">
<action method="setTemplate"><template>page/1column.phtml</template></action>
</reference>
</cms_index_index>
But this is doesn't work. How can I do this?
Homepage is a CMS page. Unfortunately, you can't assign root template for CMS pages using layout, because they have own attribute "root_template" (cms_page table).
You can change this attribute in the backend (CMS - Pages).
Or you can change it in code:
$homePage = Mage::getModel('cms/page')->load('home', 'identifier');
$homePage->setRootTemplate('one_column');
$homePage->save();
I recommend you to write sql data upgrade, which will update root template value for homepage:
$installer = $this;
/* #var $installer Mage_Core_Model_Resource_Setup */
$installer->startSetup();
$installer->run("
UPDATE `{$this->getTable('cms_page')}` SET `root_template` = 'one_column' WHERE `identifier` = 'home';
");
$installer->endSetup();
I'm sure the other suggestions work well, but that all looks way too complicated to me. What I've done which seems to work great, is to simply put the following into the Layout Update XML for the CMS page in question (in this case, your home page)
<reference name="root">
<action method="setTemplate">
<template>page/1column.phtml</template>
</action>
</reference>
The problem lays in Mage_Cms_Helper_Page::_renderPage. Layout updates are applied -before- root template (configured from the backend) is applied:
Mage::dispatchEvent('cms_page_render', array('page' => $page, 'controller_action' => $action));
$action->loadLayoutUpdates();
$layoutUpdate = ($page->getCustomLayoutUpdateXml() && $inRange) ? $page->getCustomLayoutUpdateXml() : $page->getLayoutUpdateXml();
$action->getLayout()->getUpdate()->addUpdate($layoutUpdate);
$action->generateLayoutXml()->generateLayoutBlocks();
...
if ($page->getRootTemplate()) {
$action->getLayout()->helper('page/layout')
->applyTemplate($page->getRootTemplate());
}
Also notice how the only event in this method is inconveniently placed above all of this... Should you want to fix this cleanly (without queries), you should observe the following event:
controller_action_postdispatch_cms_index_index
Then do the following (untested, but should work):
$this->getEvent()->getControllerAction()->getLayout()->helper('page/layout')->applyTemplate('one_column');
Then render the layout again. This is all just a guideline how to solve this through observers.

Resources