Don't generate log parameters unnecessarily - log4net

I have some log statements like this:
_log.InfoFormat("Log some stuff: {0}", string.Join(", ", someList));
If I set the log level below Info then these statements are omitted from the log file, but the parameters (the string.Join stuff) are still generated unnecessarily. Is there a way to do something like this:
_log.InfoFormat("Log some stuff: {0}", () => string.Join(", ", someList));
I didn't see anything in intellisense for this. Are there any good (concise) workarounds, or other frameworks that can do deferred parameter generation?

I ended up making a little extension method:
public static void InfoDeferred(this ILog source, string message, params Func<object>[] deferredArgs)
{
if(source.IsInfoEnabled)
{
var args = new List<object>();
foreach(var arg in deferredArgs)
{
args.Add(arg());
}
source.InfoFormat(message, args.ToArray());
}
}
Used like this:
_log.InfoDeferred("Log some stuff: {0}", () => string.Join(", " someList));

Related

Castle Windsor Interceptor - adding a HTTP Header

I'm trying to add an interceptor to just add a simple HTTP header, is there a nice way of doing this using IInvocation?
I've had a look around and can't see any examples of it, or via a WcfPolicy. An example of what I'm trying to do is below..
Cheers,
Jamie
public void Intercept(IInvocation invocation)
{
Guard.NotNull(() => invocation, invocation);
invocation.Proceed();
AddVersionHeaders(invocation);
}
private static void AddVersionHeaders(IInvocation invocation)
{
using (var scope = new OperationContextScope(OperationContext.Current))
{
OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = new HttpRequestMessageProperty
{
Headers =
{
{
"X-Version", invocation.TargetType.Assembly.GetName().Version.ToString()
}
}
};
}
}
In the end, just went with adding it in each Global.asax, would have been nice to have it in a library but perhaps simplicity over reuse is the best option.

Lost headers when using UnZipResultSplitter

I'm using the Spring Integration Zip extension and it appears that I'm losing headers I've added upstream in the flow. I'm guessing that they are being lost in UnZipResultSplitter.splitUnzippedMap() as I don't see anything that explicitly copies them over.
I seem to recall that this is not unusual with splitters but I can't determine what strategy one should use in such a case.
Yep!
It looks like a bug.
The splitter contract is like this:
if (item instanceof Message) {
builder = this.getMessageBuilderFactory().fromMessage((Message<?>) item);
}
else {
builder = this.getMessageBuilderFactory().withPayload(item);
builder.copyHeaders(headers);
}
So, if those splitted items are messages already, like in case of our UnZipResultSplitter, we just use message as is without copying headers from upstream.
Please, raise a JIRA ticket (https://jira.spring.io/browse/INTEXT) on the matter.
Meanwhile let's consider some workaround:
public class MyUnZipResultSplitter {
public List<Message<Object>> splitUnzipped(Message<Map<String, Object>> unzippedEntries) {
final List<Message<Object>> messages = new ArrayList<Message<Object>>(unzippedEntries.size());
for (Map.Entry<String, Object> entry : unzippedEntries.getPayload().entrySet()) {
final String path = FilenameUtils.getPath(entry.getKey());
final String filename = FilenameUtils.getName(entry.getKey());
final Message<Object> splitMessage = MessageBuilder.withPayload(entry.getValue())
.setHeader(FileHeaders.FILENAME, filename)
.setHeader(ZipHeaders.ZIP_ENTRY_PATH, path)
.copyHeaders(unzippedEntries/getHeaders())
.build();
messages.add(splitMessage);
}
return messages;
}
}

Using RazorEngine with TextWriter

I want to use RazorEngine to generate some html files. It's easy to generate strings first, then write them to files. But if the generated strings are too large, that will cause memory issues.
So I wonder is there a non-cached way to use RazorEngine, like using StreamWriter as its output rather than a string.
I google this for a while, but with no luck.
I think use a custom base template should be the right way, but the documents are so few(even out of date) on the offcial homepage of RazorEngine.
Any hint will be helpful!
OK. I figured it out.
Create a class that inherits TemplateBase<T>, and take a TextWrite parameter in the constructor.
public class TextWriterTemplate<T> : TemplateBase<T>
{
private readonly TextWriter _tw;
public TextWriterTemplate(TextWriter tw)
{
_tw = tw;
}
// override Write and WriteLiteral methods, write text using the TextWriter.
public override void Write(object value)
{
_tw.Write(value);
}
public override void WriteLiteral(string literal)
{
_tw.Write(literal);
}
}
Then use the template as this:
private static void Main(string[] args)
{
using (var sw = new StreamWriter(#"output.txt"))
{
var config = new FluentTemplateServiceConfiguration(c =>
c.WithBaseTemplateType(typeof(TextWriterTemplate<>))
.ActivateUsing(context => (ITemplate)Activator.CreateInstance(context.TemplateType, sw))
);
using (var service = new TemplateService(config))
{
service.Parse("Hello #Model.Name", new {Name = "Waku"}, null, null);
}
}
}
The content of output.txt should be Hello WAKU.

NLog MemoryTarget maximum size

Since I have a global exception handler that reports uncaught errors via e-mail, next step is to add some context to it by having some 10-20 last lines of log that are collected.
So I am using MemoryTarget like so:
MemoryTarget _logTarget;
_logTarget = new MemoryTarget();
_logTarget.Layout = "${longdate}|${level:uppercase=true}|${logger}|${message}${exception}";
LoggingRule loggingRule = new LoggingRule("*", LogLevel.Debug, _logTarget);
LogManager.Configuration.AddTarget("exceptionMemory", _logTarget);
LogManager.Configuration.LoggingRules.Add(loggingRule);
LogManager.Configuration.Reload();
Apps containing this should run forever, and if I leave logs in memory, unchecked, I'll have neatly designed memory leak.
How to address this? How to truncate MemoryTarget.Logs to have at most say 100 lines?
Your best bet is probably to write your own MemoryTarget... Something like this (untested) should work.
namespace NLog.Targets
{
using System.Collections.Generic;
[Target("LimitedMemory")]
public sealed class LimitedMemoryTarget : TargetWithLayout
{
private Queue<string> logs = new Queue<string>();
public LimitedMemoryTarget()
{
this.Logs = new List<string>();
}
public IEnumerable<string> Logs
{
get { return logs; }
private set { logs = value; }
}
[DefaultValue(100)]
public int Limit { get; set; }
protected override void Write(LogEventInfo logEvent)
{
string msg = this.Layout.Render(logEvent);
logs.Enqueue(msg);
if (logs.Count > Limit)
{
logs.Dequeue();
}
}
}
}
This example is based on the NLog MemoryTarget, the source code for which you can find here:
https://github.com/NLog/NLog
NLog docs are here:
http://nlog-project.org/documentation/v2.0.1/
I didn't see anything like you are asking about in either location.

Playframework Excel file generation

I've installed excel module in order to generate reports from datas recorded by my application into database.
It works fine : i can create report simply by clicking on a link into my main page and render into excel template.
But i'd rather generate excel file periodically (using a job) and save it into a shared folder, and that without any human action (so not by clicking on a link).
It's like I want to trigger the associated controller to render into my template automatically.
Does anyone got any tips on it for me?
So the problem is you can't pass some parameters into the job, or...?
Using something like this just doesn't work?
#On("0 45 4-23 ? * MON-FRI")
public class ExcelJob extends Job {
public void doJob() {
// generate excel
}
}
I wrote my own Excel generator using JExcel, and I use it for scheduled generation without a problem. It also doesn't require a template, because the report structure is derived from annotations. This is roughly 20 lines of code - you may want to try it for yourself.
This is really rough and lacks good user feedback, but gives you the idea...
Excel generator - not Play-specific in any way
public class ExcelGenerator
{
public void generateReport(Function successCallback,
Function failureCallback)
{
try
{
byte[] report = // generate your report somehow
successCallback.execute(report);
}
catch (Exception e)
{
failureCallback.execute(e.getMessage());
}
}
}
A function interface for callbacks (very basic)
public interface Function
{
public void execute(Object... args);
}
Your Play controller
public class MyController extends Controller
{
public static void index()
{
render();
}
public static void createReport()
{
Function failureCallback = new Function()
{
public void execute(Object... args)
{
flash.error(args[0]);
indxe();
}
};
Function successCallback = new Function()
{
public void execute(Object... args)
{
renderBinary((byte[])args[0]);
}
};
ExcelGenerator excelGenerator = new ExcelGenerator();
excelGenerator.generateReport(successCallback,
failureCallback);
}
}
Finally, re-use the ExcelGenerator from your job
public class MyJob extends Job
{
public void doJob()
{
Function failureCallback = new Function()
{
public void execute(Object... args)
{
Logger.error(args[0]);
}
}
Function successCallback = new Function()
{
public void execute(Object... args)
{
byte[] report = (byte[])args[0];
// write report to disk
}
}
ExcelGenerator excelGenerator = new ExcelGenerator();
excelGenerator.generateReport(successCallback,
failureCallback);
}
}
You'll still need to write your own report generator, or refactor the existing excel module to provide what you need.
So if you want to run and manage several jobs you can do something like this
for (int i = 0; i < 10; i++) {
SendingMessageJob sendingMessageJob = new SendingMessageJob();
promises.add(sendingMessageJob.now());
}
boolean allDone = false;
while (!allDone) {
allDone = true;
for (F.Promise promise : promises) {
if (!promise.isDone()) {
allDone = false;
break;
}
}
}
// when arrive here all jobs have finished their process
You can check the Play documentation, specifically the section on jobs, where you'll see examples on how to create automatically triggered methods. This should solve your issue.
EDIT (update on comment):
You can manually trigger a job, do this:
new MyExcelGeneratorJob().doJob();
Thing is, Play is stateless, so the job should use data from the database. Instead of trying to pass parameters from your request into the Job (won't work) try to store that data in a staging area in the database that the job loads and processes to generate the excel.

Resources