Extended properties in NLog - asp.net-core

Layout mentioned in config file is as follows:
Timestamp: ${date}${newline}Title: ${event-properties:item=Title}${newline}Message: ${message}${newline}Machine: ${machinename}${newline}${newline}${LayoutFooter}
On exception, I want to add two more properties to this layout, which includes Stack Trace and Inner Exception Message.
I am achieving the above requirement, by modifying the layout pattern to:
Timestamp: ${date}${newline}Title: ${event-properties:item=Title}${newline}Message: ${message}${newline}${event-properties:item=StackTrace}${event-properties:item=InnerException}Machine: ${machinename}${newline}${newline}${LayoutFooter}
And then through code,
private static void WriteLog(LogEvent logEvent)
{
var log = LogManager.GetLogger(logEvent.Logger);
LogEventInfo logMsg = new LogEventInfo();
logMsg.Message = logEvent.Message;
logMsg.Level = logEvent.LogLevel;
logMsg.Properties.Add("Title", logEvent.Title);
if(!string.IsNullOrEmpty(logEvent.StackTrace))
{
logMsg.Properties.Add("StackTrace", "Stack Trace: " + logEvent.StackTrace + Environment.NewLine);
}
if(!string.IsNullOrEmpty(logEvent.InnerException))
{
logMsg.Properties.Add("InnerException", "Inner Exception: " + logEvent.InnerException + Environment.NewLine);
}
log.Log(logMsg);
}
By following above approach, if I need to add more extended properties, I need to change config file and code.
In case of single config file, this approach is fine, but in case of multiple config files, it is time consuming.
Is there any way, through which I can add extended properties only by changing code, and no change in config file.
I was able to achieve this functionality, when I was using Microsoft Enterprise Logging library, as it had ExtendedProperties property in LogEntry class of Microsoft.Practices.EnterpriseLibrary.Logging.

Is there any way, through which I can add extended properties only by changing code, and no change in config file.
There is a ${all-event-properties} renderer and it has multiple parameters how to render.

Related

Dependency Injection Access While Configuring Service Registrations in asp.net Core (3+)

I have cases, where I want to configure services based on objects which are registered in the dependency injection container.
For example I have the following registration for WS Federation:
authenticationBuilder.AddWsFederation((options) =>{
options.MetadataAddress = "...";
options.Wtrealm = "...";
options.[...]=...
});
My goal in the above case is to use a configuration object, which is available via the DI container to configure the WsFederation-middleware.
It looks to me that IPostConfigureOptions<> is the way to go, but until now, I have not found a way to accomplish this.
How can this be done, or is it not possible?
See https://andrewlock.net/simplifying-dependency-injection-for-iconfigureoptions-with-the-configureoptions-helper/ for the I(Post)ConfigureOptions<T> way, but I find that way too cumbersome.
I generally use this pattern:
// Get my custom config section
var fooSettingsSection = configuration.GetSection("Foo");
// Parse it to my custom section's settings class
var fooSettings = fooSettingsSection.Get<FooSettings>()
?? throw new ArgumentException("Foo not configured");
// Register it for services who ask for an IOptions<FooSettings>
services.Configure<FooSettings>(fooSettings);
// Use the settings instance
services.AddSomeOtherService(options => {
ServiceFoo = fooSettings.ServiceFoo;
})
A little more explicit, but you have all your configuration and DI code in one place.
Of course this bypasses the I(Post)ConfigureOptions<T> entirely, so if there's other code that uses those interfaces to modify the FooSettings afterwards, my code won't notice it as it's reading directly from the configuration file. Given I control FooSettings and its users, that's no problem for me.
This should be the approach if you do want to use that interface:
First, register your custom config section that you want to pull the settings from:
var fooSettingsSection = configuration.GetSection("Foo");
services.Configure<FooSettings>(fooSettingsSection);
Then, create an options configurer:
public class ConfigureWSFedFromFooSettingsOptions
: IPostConfigureOptions<Microsoft.AspNetCore.Authentication.WsFederation.WsFederationOptions>
{
private readonly FooSettings _fooSettings;
public ConfigureWSFedFromFooSettingsOptions(IOptions<FooSettings> fooSettings)
{
_fooSettings = fooSettings.Value;
}
public void Configure(WsFederationOptions options)
{
options.MetadataAddress = _fooSettings.WsFedMetadataAddress;
options.Wtrealm = _fooSettings.WsFedWtRealm;
}
}
And finally link the stuff together:
services.AddTransient<IPostConfigureOptions<WsFederationOptions>, ConfigureWSFedFromFooSettingsOptions>();
The configurer will get your IOptions<FooSettings> injected, instantiated from the appsettings, and then be used to further configure the WsFederationOptions.

Can't upload files in spring boot

I've been struggling with this for the past 3 days now, I keep getting the following exception when I try upload a file in my spring boot project.
org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'file' is not present
I'm not sure if it makes a differance but I am deploying my application as a war onto weblogic,
here is my controller
#PostMapping
public AttachmentDto createAttachment(#RequestParam(value = "file") MultipartFile file) {
logger.info("createAttachment - {}", file.getOriginalFilename());
AttachmentDto attachmentDto = null;
try {
attachmentDto = attachmentService.createAttachment(new AttachmentDto(file, 1088708753L));
} catch (IOException e) {
e.printStackTrace();
}
return attachmentDto;
}
multi part beans I can see in spring boot actuator
payload seen in chrome
Name attribute is required for #RequestParm 'file'
<input type="file" class="file" name="file"/>
You can try use #RequestPart, because it uses HttpMessageConverter, that takes into consideration the 'Content-Type' header of the request part.
Note that #RequestParam annotation can also be used to associate the part of a "multipart/form-data" request with a method argument supporting the same method argument types. The main difference is that when the method argument is not a String, #RequestParam relies on type conversion via a registered Converter or PropertyEditor while #RequestPart relies on HttpMessageConverters taking into consideration the 'Content-Type' header of the request part. #RequestParam is likely to be used with name-value form fields while #RequestPart is likely to be used with parts containing more complex content (e.g. JSON, XML).
Spring Documentation
Code:
#PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public AttachmentDto createAttachment(#RequestPart("file") MultipartFile file) {
logger.info("Attachment - {}", file.getOriginalFilename());
try {
return attachmentService.createAttachment(new AttachmentDto(file, 1088708753L));
} catch (final IOException e) {
logger.e("Error creating attachment", e);
}
return null;
}
You are using multi part to send files so there is nothing much configuration to do to get desired result.
I m having the same requirement and my code just run fine :
#RestController
#RequestMapping("/api/v2")
public class DocumentController {
private static String bucketName = "pharmerz-chat";
// private static String keyName = "Pharmerz"+ UUID.randomUUID();
#RequestMapping(value = "/upload", method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA)
public URL uploadFileHandler(#RequestParam("name") String name,
#RequestParam("file") MultipartFile file) throws IOException {
/******* Printing all the possible parameter from #RequestParam *************/
System.out.println("*****************************");
System.out.println("file.getOriginalFilename() " + file.getOriginalFilename());
System.out.println("file.getContentType()" + file.getContentType());
System.out.println("file.getInputStream() " + file.getInputStream());
System.out.println("file.toString() " + file.toString());
System.out.println("file.getSize() " + file.getSize());
System.out.println("name " + name);
System.out.println("file.getBytes() " + file.getBytes());
System.out.println("file.hashCode() " + file.hashCode());
System.out.println("file.getClass() " + file.getClass());
System.out.println("file.isEmpty() " + file.isEmpty());
/**
BUSINESS LOGIC
Write code to upload file where you want
*****/
return "File uploaded";
}
None of the above solutions worked for me, but when I digged deeper i found that spring security was the main culprit. Even if i was sending the CSRF token, I repeatedly faced the issue POST not supported. I came to know that i was receiving forbidden 403 when i inspected using developer tools in google chrome and saw the status code in the network tab. I added the mapping to ignoredCsrfMapping in Spring Security configuration and then it worked absolutely without any other flaw. Don't know why i was not allowed to post multipart data by security. Some of the mandatory setting that needs to be stated in application.properties file are as follows:
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB
spring.http.multipart.max-file-size=10MB
spring.http.multipart.max-request-size=10MB
spring.http.multipart.enabled=true

CRM 2011 - ITracingService getting access to the traceInfo at runtime

I have some custom logging in my plugin and want to include the contents of my tracingService in my custom logging (which is called within a catch block, before the plugin finishes).
I cant seem to access the content of tracingService. I wonder if it is accessible at all?
I tried tracingService.ToString() just incase the devs had provided a useful overload, alas as expected I get name of the class "Microsoft.Crm.Sandbox.SandboxTracingService".
Obviously Dynamics CRM makes use of the tracingService content towards the end of the pipeline if it needs to.
Anybody have any ideas on this?
Kind Regards,
Gary
The tracing service does not provide access to the trace text during execution but that can be overcome by creating your own implementation of ITracingService. Note, you cannot get any text that was written to the trace log prior to the Execute method of your plugin being called - meaning if you have multiple plugins firing you won't get their trace output in the plugin that throws the exception.
public class CrmTracing : ITracingService
{
ITracingService _tracingService;
StringBuilder _internalTrace;
public CrmTracing(ITracingService tracingService)
{
_tracingService = tracingService;
_internalTrace = new StringBuilder();
}
public void Trace(string format, params object[] args)
{
if (_tracingService != null) _tracingService.Trace(format, args);
_internalTrace.AppendFormat(format, args).AppendLine();
}
public string GetTraceBuffer()
{
return _internalTrace.ToString();
}
}
Just instantiate it in your plugin passing in the CRM provided ITracingService. Since it is the same interface it works the same if you pass it to other classes and methods.
public class MyPlugin : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
var tracingService = new CrmTracing((ITracingService)serviceProvider.GetService(typeof(ITracingService)));
tracingService.Trace("Works same as always.");
var trace = tracingService.GetTraceBuffer();
}
}
To get the traceInfo string from traceService at runtime I used debugger to interrogate the tracingService contents.
So the trace string is accessible from these expressions...
for Plugins
((Microsoft.Crm.Extensibility.PipelineTracingService)(tracingService)).TraceInfo
for CWA
((Microsoft.Crm.Workflow.WorkflowTracingService)(tracingService)).TraceInfo
You can drill into the tracing service by debugging and extract the expression.
However, at design time neither of these expressions seem to be accessible from any of the standard CRM 2011 SDK dlls... so not sure if its possible as yet.

When creating a T4 template, how can the app config and other file resources be utilized?

I have a T4 template that I am trying to create that will code gen lookup values from a database via Nhibernate. My problem is my data access layer uses the path of the Nhibernate configuration in order to compile the configuration upon startup (a static constructor).
I don't know how to make t4 "see" this file path so that when my code gen runs it can get this configuration file. Nor do I know how to make t4 "see" my configuration manager class; which contains the app setting that lists the path to the nhibernate xml file.
I have two configuration files, one for SQL Server and one for sqlite. The configuration file needs to be in the root directory of the executing assembly in order to nhibernate to compile the configuration.
It seems like my template won't be able to use the high level business layer to select the data from the database, rather I may have to copy all the nhibernate configuration code into the template as well (ugh).
My DB wrapper:
private class DBSingleton
{
static DBSingleton()
{
string path = ConfigurationManager.AppSettings["DBConfigFileFullPath"];
NHibernate.Cfg.Configuration cfg = new NHibernate.Cfg.Configuration().Configure(path);
cfg.AddInputStream(HbmSerializer.Default.Serialize(System.Reflection.Assembly.GetAssembly(typeof(Plan))));
instance = cfg.BuildSessionFactory();
}
internal static readonly ISessionFactory instance;
}
And my template:
<#
System.Diagnostics.Debugger.Launch();
string path = Host.ResolvePath(#"..\DB.UnitTest\App.config");
System.Configuration.ConfigurationManager.AppSettings.Add(
"DBConfigFileFullPath", path);
//System.Configuration.ConfigurationFileMap fileMap = new ConfigurationFileMap(path); //Path to your config file
//System.Configuration.Configuration configuration = System.Configuration.ConfigurationManager.OpenMappedMachineConfiguration(fileMap);
ReferenceValueBL bl = new ReferenceValueBL();
List<ReferenceValue> refVals = bl.Select(); <- problem occurs here
foreach(ReferenceValue rv in refVals)
{
Write("Public ");
Write(rv.ReferenceValueCode.GetType().Name);
Write(" ");
Write(rv.ReferenceValueCode);
Write(" = ");
Write(rv.ReferenceValueCode);
}
#>
My problem occurs when the bl variable tries to call select(). That's when the DBSingleton is initialized. It throws an error saying the app setting is null. If I hard code the relative file path in the DB class to just be ./Dbconfig.xml it still throws an error because the executing assembly doesn't have that file in it's local directory.
How do other people handle getting t4 to use app/web config files without reading from the config file within the template and then injecting a connection string into the DAL? I don't have that luxury. The file has to be either placed in a readable location or t4 has to know to look somewhere.
How do other people handle getting t4 to use app/web config files without reading from the config file within the template and then injecting a connection string into the DAL?
You could perhaps convert your text template into runtime text template by setting Properties | Custom Tool to "TextTemplatingFilePreprocessor".
Afterwards the entire code generation process would be encapsulated within standard class which will have TransformText method which produces the resulting string - you can then write it into file of your liking.
Nice thing is that the template class is partial, so you can add implementation of some custom methods. Something like this perhaps:
public partial class RuntimeTextTemplate1
{
public string TransformText(string someParameter)
{
// Do something with someParameter, initialize class field
// with its value and later use this field in your t4 file.
// You also have access to ConfigurationManager.AppSettings here.
return TransformText();
}
}
Also, this:
foreach(ReferenceValue rv in refVals)
{
Write("Public ");
Write(rv.ReferenceValueCode.GetType().Name);
Write(" ");
Write(rv.ReferenceValueCode);
Write(" = ");
Write(rv.ReferenceValueCode);
}
Could be rewritten as:
foreach(ReferenceValue rv in refVals)
{
#>
Public <#= rv.ReferenceValueCode.GetType().Name #> <#= rv.ReferenceValueCode #> = <#= rv.ReferenceValueCode #>
<#+
}

dynamic file path in log4php

I am new to log4php.
I would like to save the log files in the format /logs/UserId/Info_ddmmyyyy.php
where the UserId is dynamic data.
(I would basically like to save one log per user.)
Is there any way to change the log file path dynamically?
This behaviour is not supported by default. But you can extend LoggerAppenderFile (or RollingFile, DailyFile whatever your preference is) to support it.
Create your own class for that and make it load to your script.
Then extend from this class:
http://svn.apache.org/repos/asf/logging/log4php/trunk/src/main/php/appenders/LoggerAppenderFile.php
class MyAppender extends LoggerAppenderFile { ... }
You'll need to overwrite the setFile() method, similar to:
public function setFile($file) {
$path = getYourFullPath();
$this->file = $path.$file;
}
After all you need to use your new Appender in you config
log4php.appender.myAppender = MyAppender
log4php.appender.myAppender.layout = LoggerLayoutSimple
log4php.appender.myAppender.file = my.log
Please note, instead of giving your full path to the log file you now need to add a plain name. The full path (including username) must be calculated with your getYourFullPath() method.
Hope that helps!
Christian