问题
Im testing out the new structured logging but do not really get it right.
I have this in my nlog.config :
<target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}.log">
<layout xsi:type="JsonLayout" includeAllProperties="true">${longdate}|${level}|${logger}|${message}</layout>
</target>
<logger name="CommunicationLogger" minlevel="Info" writeto="f"></logger>
My log code looks like this :
public void LogCommunication(string operation, List<object> args)
{
var parameters = new List<object>();
var text = "Operation:{Operation} ";
parameters.Add(operation);
text += "PersonId:{PersonId} ";
parameters.Add(SharedContext.GetMyUserContext().CurrentPersonId);
text += "ClientMachineName:{ComputerName} ";
parameters.Add(SharedContext.GetMyUserContext().ClientMachineName);
text += "Servername:{MachineName} ";
parameters.Add(Environment.MachineName);
if (args != null)
{
foreach(var param in args)
{
text += "Param:{@Parameters} ";
parameters.Add(param);
}
}
_log.LogCommunication(text, parameters.ToArray());
}
public void LogCommunication(string message, params object[] args)
{
_comLogger.Log(LogLevel.Info, message, args);
}
The output looks something like this :
{ "Operation": "OperationName", "PersonId": 1, "ComputerName": "MyComputername", "MachineName": "MyMachinename", "Parameters": {"LocationKeyList":[], "MyObjectIdList":[], "RolList":[]} }
I would like the Parameters to also get serialize instead of just showint [] so I can see all the parameters of the service operation. The parameter is a complex type with dataContract(WCF).
Is there a simple way to get the parameters to work with structural data.
Retagards
Update1
nlog.config
<target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}.log">
<layout xsi:type="JsonLayout" includeAllProperties="true" maxRecursionLimit="10">
<attribute name="time" layout="${longdate}" />
<attribute name="level" layout="${level}"/>
<attribute name="message" layout="${message}" />
</layout>
</target>
Code to setup data
var parameters = new List<object>();
var text = "{TimeStamp} - ";
parameters.Add(DateTime.Now);
text += "Duration:{Duration} ";
parameters.Add(Timestamp);
text += "Operation:{Operation} ";
parameters.Add(operation);
text += "PersonId:{PersonId} ";
parameters.Add(SharedContext.GetMyUserContext().CurrentPersonId);
text += "ClientMachineName:{ComputerName} ";
parameters.Add(SharedContext.GetMyUserContext().ClientMachineName);
text += "Servername:{MachineName} ";
parameters.Add(Environment.MachineName);
if (args != null && args.Count() > 0)
{
text += "Param:{@Parameters} ";
parameters.Add(args);
}
_log.LogCommunication(text, parameters.ToArray());
result :
ientMachineName:\"MyComputer\" Servername:\"MyComputer\" Param:[\"MyApp.ServiceContracts.GetEntityViewRequest\"] ", "TimeStamp": "2020-04-08T23:30:59.7725147Z", "Duration": "00:00:00.0009930", "Operation": "GetReferenceData", "PersonId": 1, "ComputerName": "SE-MyCom", "MachineName": "SE-MyCom", "Parameters": ["MyApp.ServiceContracts.GetEntityViewRequest"] }
{ "time": "2020-04-09 01:31:00.3637", "level": "Info", "message": "2020-04-09 01:31:00 - Duration:00:00:00.5594936 Operation:\"GetExternaAnrop\" PersonId:1 ClientMachineName:\"MyComputer\" Servername:\"MyComputer\" Param:[{\"PlaceringKeyList\":[], \"ArbetsstalleIdList\":[], \"RollList\":[]}] ", "TimeStamp": "2020-04-08T23:31:00.363752Z", "Duration": "00:00:00.5594936", "Operation": "GetExternaAnrop", "PersonId": 1, "ComputerName": "SE-MyCom", "MachineName": "SE-MyCom", "Parameters": [{"PlaceringKeyList":[], "ArbetsstalleIdList":[], "RollList":[]}] }
Update2
I have a service method that looks like this :
GetEntityViewResponse GetReferenceData(GetEntityViewRequest request);
The request class looks like this :
[DataContract]
public class GetEntityViewRequest
{
public GetEntityViewRequest(params EntityViewKey[] Keys)
{
EntityViewKeys.AddRange(Keys);
}
public GetEntityViewRequest(params int[] EgnaKodtyper)
{
MyList.AddRange(EgnaKodtyper);
}
public GetEntityViewRequest()
{
}
[DataMember]
public List<EntityViewKey> EntityViewKeys = new List<EntityViewKey>();
[DataMember]
public List<int> MyList= new List<int>();
}
When running the code(sending the request) I can see in my messageInspector on the serviceside that I got data. The EntityViewKeys have a enum set.
The NLog output looks like this :
{ "time": "2020-04-12 19:27:55.6690", "level": "Info", "message": "2020-04-12 19:27:55 - Duration:00:00:00.0034730 Operation:\"GetReferenceData\" PersonId:1 ClientMachineName:\"MyComputer\" Servername:\"MyComputer\" Param:[\"Orbit.ServiceContracts.GetEntityViewRequest\"] ", "TimeStamp": "2020-04-12T17:27:55.6690745Z", "Duration": "00:00:00.0034730", "Operation": "GetReferenceData", "PersonId": 1, "ComputerName": "SE-MyCom", "MachineName": "SE-MyCom", "Parameters": ["Orbit.ServiceContracts.GetEntityViewRequest"] }
So even if the class is not complex it is still not printing the content in NLog?
回答1:
There is an important parameter for JsonLayout called MaxRecursionLimit
(Default = 0 until NLog v5 arrives):
https://github.com/nlog/nlog/wiki/JsonLayout
So you can do this:
<layout xsi:type="JsonLayout" includeAllProperties="true" maxRecursionLimit="10">
<attribute name="time" layout="${longdate}" />
<attribute name="level" layout="${level}"/>
<attribute name="message" layout="${message}" />
</layout>
Maybe you want to change your parameters into this:
if (args != null)
{
text += "Params:{@Parameters} ";
parameters.Add(args);
}
NLog will by default not serialize object-fields, but only object-properties. If fields are a must, then you can setup setup custom reflection with NLog 4.7:
LogManager.Setup().SetupSerialization(s =>
s.RegisterObjectTransformation<GetEntityViewResponse>(obj => new {
EntityViewKeys = obj.EntityViewKeys,
MyList = obj.MyList,
})
);
Or if you are a lazy person:
LogManager.Setup().SetupSerialization(s =>
s.RegisterObjectTransformation<GetEntityViewRequest>(obj =>
return Newtonsoft.Json.Linq.JToken.FromObject(obj) // Lazy and slow
)
);
来源:https://stackoverflow.com/questions/61103712/how-to-serialize-object-fields-with-nlog