how to specify log based on log level in serilog? - c#

I'm using Serilog as logging framework in my project. My question is as it states in the Title. I want different logs based on my minimum level.
I'm using LoggingLevelSwitch for dynamic log levels while the app is working but I want to customize the log itself based on this switch.
For example if my level is debug I want to show the whole object in my log but if it is information level then I just want to show lesser detail about my object or even better change the whole message template.
Something similar like this below:
if(logLevelIsAboveDebug)//inf, wrn, err, ftl
logger.Information(msgTemplateCodes.INF001,myObject.someProperty);
else
logger.Debug(msgTemplateCodes.DBG001, myObject, secondVariable)
and my template string would be something like this.
INF001 => "Some detail about action. {userKnownVariableGoesHere}"
DBG001 => "Yet another log for same action with more detail. {#myCustomObject} {someVariableDoesntMatter}"
Since not all of my logs are using the same template I guess I can't just configure this at start. I decided to make a method that will take a parameter and check. Something like this:
public static bool isLoglevelEqualOrAboveGiven(LogEventLevel givenLevel)
{
if ((int)givenLevel <= (int)logLevel.MinimumLevel) return true;
else return false;
}
And now I can just use this method to check which log I should use but is this right?
I don't want to log the whole object if it's in information level I only want the whole object in case something happen and I'm running the app in debug log level in production.
Is this approach correct? should I continue with this? will it cause any problem in my app to check this is many places?

Related

Logging to Azure Log Analytics custom fields using Serilog

I'm new to azure and also a rookie in .net, so this may be a noob question :)
Anyway, I have successfully managed to log to Azure Log Analytics using this code snippet:
var loggerConfig = new LoggerConfiguration().
Enrich.WithExceptionDetails().
Enrich.WithApplicationInformation(serviceName).
MinimumLevel.Debug().
MinimumLevel.Override("Microsoft", LogEventLevel.Information).
Enrich.FromLogContext().
Destructure.ByTransforming<ExpandoObject>(JsonConvert.SerializeObject).
//Enrich.WithProperty("ErrorMsg_CF","test").
WriteTo.AzureAnalytics(workspaceId: "MyWorkSpaceID",
authenticationId: "MyAuthID",
logName: "MyCustomLog_CL",
restrictedToMinimumLevel: LogEventLevel.Debug)
And:
Logging.Logger.Initialize(serviceName: "Logging Web Test v1.1");
Logging.Logger.GetLogger().Log(LogLevel.Debug, "{ErrorMsg_CF}:{TraceID_CF}:{UserName_CF}", errorMsg,traceID,userName);
I have, in my Log Analytics custom log table, created 3 custom fields: "ErrorMsg_CF", "TraceID_CF" and "UserName_CF", and I want to either be able to directly log to these custom fields, or somehow split my errormsg into these three custom fields.
When I import a file, I see that the message is stored in the "RawData" field, and with that field I am able to use the custom field generator and create custom fields, and also let Log Analytics split the message into the fields for me. But, when I do it through my application, the message is stored in the "LogMEssage_s" field, and seems like it's not possible to create custom fields from that field.
So, anyone know how I can log to my custom fields from my application?
The default formatting configuration is a line by line event logging. ALN can be ingested using JSON so that will be the best way to format log entry in that way.
.WriteTo.DESTINATION(new CompactJsonFormatter(), OTHER OPTIONS HERE)
Serilog formatting

How to add a parameter to .Net Core structured logging without referencing it in a message?

I can do this in .net core
_logger.LogInformation("Token validated {clientId}", "MyId");
And then logging libraries like NLog will know that there is a property called clientId with the value MyId in the message and can render it in a special way.
I am trying to do the same without including the property in the message itself, but cannot manage to nail it. This is what I have done so far and it does not result in a property in NLog:
LogEventInfo info = new LogEventInfo
{
Properties = {{"clientId", "MyId"}},
};
_logger.Log(Microsoft.Extensions.Logging.LogLevel.Information, "Token validated", info, null, info.MessageFormatter);
This results in a message without property. Is there a better way to do this or have I done something wrong?
The whole idea with Microsoft-Extension-Logging (MEL) ILogger-interface is not being dependent on a specific Logging-Framework.
If you start creating NLog LogEventInfo-objects, then you might as well call NLog.LogManager.GetCurrentClassLogger() and use that as Logger.
But maybe this wiki-page can give you some ideas:
https://github.com/NLog/NLog.Extensions.Logging/wiki/NLog-properties-with-Microsoft-Extension-Logging

How to verify user role before executing action?

I'm working on a project in which some users can be in the role AdminReader. Those users can see everything, but will not be able to save/edit any data.
I know I can do it this way:
public JsonResult ChangeStatus(int? id)
{
// AdminReader validation
if (base.User.isAdminReader)
{
return Json(new
{
Message = "You don't have privileges to alter data.",
Success = false,
}, JsonRequestBehavior.AllowGet);
}
// Function code
But I don't want to insert the above code inside all project functions.
I thought I could decorate my methods like we use [HttpGet]. I've also read this SO post.
Then I dropped the idea.
But then I found about Exception Handler Attribute and a logging action filter.
Is it possible to somehow combine the public void OnActionExecuting(ActionExecutingContext filterContext) with my AdminReader validation?
I don't know if it is the right way to go about my problem. Also, I'm not sure it could work really. What's the best practice in this situation?
Any suggestion is welcome, thanks in advance.
There are many ways to do this.
Yes, it's true that attributes are just metadata. However, the MVC framework has code in it that recognizes certain metadata and performs actions on it. Examples include the two attributes you mentioned (ActionFilters and ExceptionFilters), there's also AuthorizationFilters, which may be what you actually want.
AuthorizationFilters run before ActionFilters, near the start of the MVC pipeline, which allows them to block access before the page actually renders. But, if you don't need that, you can just use this point to do specific things before the page renders.
However, having said that, you are still going to need to have code on each page that controls what the user can and can't do based on their role. There is no magic way around that. Whenever you want to control what a user can do on a page based on access, you need code that does that in each section where control is required.
It's not clear from your example what you are trying to do, since the return value from a page is typically the HTML to render, but it looks like you want to return some kind of status message. I don't see how that can be replicated to all pages, since the pages themselves need to render.
I'm not entirely sure I understood your question, so sorry if this is off: but if you wanted to perform your AdminReader logic, you could write your own custom attribute like below:
public class AccessDeniedAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
if (filterContext.Result is HttpUnauthorizedResult)
{
// Perform your unauthorized action here.
}
}
}
And then throw the attribute on any method where it applies (or you could throw it on the entire Controller class, if it applied to everything). Like so:
// The RoleSettings is a class of constants I defined that just contain strings
[AccessDeniedAuthorize(Roles = RoleSettings.AdminRole]
[HttpPost]
public ActionResult MyEditMethod()
{
// Perform actions if they are in the AdminRole
// If not authorized, it will do whatever you defined above in the
// AccessDeniedAuthorizeAttribute
}

ajax submit to WebAPI controller

I'm not sure of the best way to accomplish my goal. Looking for insight. I'm familiar with WebAPI services consumed through WPF and Silverlight but this is my first run at ASP and MVC.
I am building a site to verify contents of a shipment against an electronic manifest (EDI 856). I have a page that displays the shipping data and I need the users to scan each item barcode in the container. I would then like to pass that barcode to a service, verify the item belongs in that shipment and then update the page to show as much.
My plan was to have a single text box into which the user could scan/type the barcode and then submit that data to a WebAPI service which would verify the information and then probably use SignalR to send a message back to the page and update a grid with the item data.
If this is a decent way to go, I'm just not quite sure how to use ajax to call the WebAPI endpoint and provide the data I need.
I would advise against using SignalR in this situtation. What you need, judging from your description, is the most basic use case of submitting an ajax request and receiving a response.
You are not designing a system where you need the server to initiate communication with the browser or anything like that, where sockets (and SignalR as an abstraction over sockets with fallbacks to less suitable protocols) is a huge overkill.
Don't worry, your use case is rather simple.
It's a little out of scope to describe how to setup a WebApi project, how to configure routing, action names, etc. Simple google searches will surely provide ample quality tutorials on getting started.
I'll just try to explain what the general idea is, with some code samples, to get you thinking in the right direction.
You need to create an ApiController.
The simplest version of that Controller will probably look something like this:
public class ShipmentVerificationController : ApiController
{
//this is the response object you will be sending back to the client website
public class VerificationResult
{
public bool Valid;
}
public VerificationResult GetIsItemValid(string BarCode)
{
bool itemIsValid;
// Implement checks against the BarCode string here
itemIsValid = true;
return new VerificationResult { Valid = itemIsValid };
}
}
Note that the inner class represents the response you will be sending back. It should be properly filled out with additional info if needed and probably put into a separate .cs file in the "Models" folder or where ever you see fit.
I have declared it inside the controller for demonstration purposes only
Once you have a WebApi service deployed, it's really easy to send it data from your website and receive the feedback.
To simplify Ajax requests, jQuery is often used.
Once the user inputs the barcode into a textbox, you can hook up an event to check for return key being pressed (most barcode scanners send the return key command after they input the barcode data) and then write something along the lines of:
var barcode = $("#input-field").val();
$.getJSON( "<url_to_your_webapi_service>/api/ShipmentVerification/GetIsItemValid/" + barcode, function( data ) {
if (data.Valid) {
// great, highlight the item as valid
}
else {
//better indicate an error with the scanned item
}
});
Please note that for simplicity I have not included any error handling, url parameter encoding, and most importantly, zero authorization.
Authorization is very important if you deploy the web service to the open web but still do not want anyone to be able to call it.
You will have to research these topics yourself, but I hope I have presented you the core concepts and logic behind a simple service such as this, so you have a base to start with.
If you come up with specific problems and questions post a new question.
I actually found a more simple way to do this. I nixed the idea of using a WebAPI endpoint and just went with a normal controller. I used ajax to prevent the page from refreshing with the new view, since that view is actually just json data with my return values in it.

How can i add logs of user's activity to a table in XAF?

I have a Log table and I need to add user's activities (especially security related).
For instance, If a user changes her password or logs in to the software then i will add "password changed" or "user logged in" string to a table. I think I should override the default password change action as explained here
Am I correct and what is the corresponding action? Is it provided by XAF? I couldn't find anything in the documentation. In fact, I also don't know how to add it to the table, I only logged some text to a txt file until now.
Like #shamp00 says above, The Audit Trail Module does this already and quite well. The audit trail module when enabled logs everything so all you have to do is write code to display only the information you want :-)
Now unfortunately the password property is readonly so cannot be accessed but you can monitor all other user changes including a password reset since this triggers a change in the "change password on next logon" property.
All you need to do is
Add the Audit Trail Module to your project.
Add this snippet to the specific object you would like to audit
private XPCollection<AuditDataItemPersistent> userAuditTrail;
public XPCollection<AuditDataItemPersistent> UserAuditTrail
{
get
{
if(userAuditTrail == null)
{
userAuditTrail = AuditedObjectWeakReference.GetAuditTrail(Session, user);
}
return userAuditTrail;
}
}
To get this to work for me, I created an object called employee that inherited from DevExpress.ExpressApp.Security.Strategy.SecuritySystemUser because we want to extend the SecuritySystemUser to not only add full employee details (first name, last name and address details) but also to add auditing. Now because the password property is read-only, we cannot audit changes to it.
Since you are using Devexpress Universal you have the source code and so you can change the bits you don't like and recompile them to suit your needs. Consider putting adding the snippet below to the "change my password action".
AuditDataItem customDataItem = new AuditDataItem(myAuditedObject,
null,
"CustomOldValue", "CustomNewValue", AuditOperationType.CustomData);
AuditTrailService.Instance.AddCustomAuditData(mySession,
customDataItem); AuditTrailService.Instance.SaveAuditData(mySession);
Then for the time-stamp, you can extend the controller for each event you would like to log (like the logon) and add the time to your audit log using the snippet above.
I hope this helps you. I will continue to look into it though.
This is what the audit trail module does.
If you need to add custom data to the audit trail, see the section named Add Custom Data to the Audit Log.

Categories

Resources