Skip to content

Commit babe19e

Browse files
authored
Transition to JSON logging for the Linux Dedicated plan (#1721)
1 parent eb1150c commit babe19e

File tree

4 files changed

+124
-285
lines changed

4 files changed

+124
-285
lines changed

release_notes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
## New Features
2+
- Improved supportability logs on Linux Dedicated plans (https://github.com/Azure/azure-functions-durable-extension/pull/1721)
23

34
## Bug fixes
45
- Fix issue with local RPC endpoint used by non-.NET languages on Windows apps (#1800)

src/WebJobs.Extensions.DurableTask/LinuxAppServiceLogger.cs

Lines changed: 6 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -22,86 +22,11 @@ internal class LinuxAppServiceLogger
2222
{
2323
private const string ConsolePrefix = "MS_DURABLE_FUNCTION_EVENTS_LOGS";
2424

25-
// The ordered list of regex columns in our legacy logging strategy for linux dedicated.
26-
private readonly string[] orderedRegexCol = new string[]
27-
{
28-
"Account",
29-
"ActiveActivities",
30-
"ActiveOrchestrators",
31-
"Age",
32-
"AppName",
33-
"ContinuedAsNew",
34-
"CreatedTimeFrom",
35-
"CreatedTimeTo",
36-
"DequeueCount",
37-
"Details",
38-
"Duration",
39-
"ETag",
40-
"Episode",
41-
"EventCount",
42-
"EventName",
43-
"EventType",
44-
"Exception",
45-
"ExceptionMessage",
46-
"ExecutionId",
47-
"ExtensionVersion",
48-
"FromWorkerName",
49-
"FunctionName",
50-
"FunctionState",
51-
"FunctionType",
52-
"Input",
53-
"InstanceId",
54-
"IsCheckpointComplete",
55-
"IsExtendedSession",
56-
"IsReplay",
57-
"LastCheckpointTime",
58-
"LatencyMs",
59-
"MessageId",
60-
"MessagesRead",
61-
"MessagesSent",
62-
"MessagesUpdated",
63-
"NewEventCount",
64-
"NewEvents",
65-
"NextVisibleTime",
66-
"OperationId",
67-
"OperationName",
68-
"Output*",
69-
"PartitionId",
70-
"PendingOrchestratorMessages",
71-
"PendingOrchestrators",
72-
"Reason",
73-
"RelatedActivityId",
74-
"RequestCount",
75-
"RequestId",
76-
"RequestingExecutionId",
77-
"RequestingInstance",
78-
"RequestingInstanceId",
79-
"Result",
80-
"RuntimeStatus",
81-
"SequenceNumber",
82-
"SizeInBytes",
83-
"SlotName",
84-
"StatusCode",
85-
"StorageRequests",
86-
"Success",
87-
"TableEntitiesRead",
88-
"TableEntitiesWritten",
89-
"TargetExecutionId",
90-
"TargetInstanceId",
91-
"TaskEventId",
92-
"TaskHub",
93-
"Token",
94-
"TotalEventCount",
95-
"Version",
96-
"VisibilityTimeoutSeconds",
97-
"WorkerName",
98-
};
99-
10025
// variable below is internal static for testing and other convenient purposes
10126
// we need to be able to change the logging path for a windows-based CI
10227
// the logger being internal static is convenient for flushing it
10328
#pragma warning disable SA1401 // Fields should be private
104-
internal static string LoggingPath = "/var/log/functionsLogs/durableevents.log";
29+
internal static string LoggingPath = "/var/log/functionsLogs/durableeventsJSON.log";
10530
internal static LinuxAppServiceFileLogger Logger; // The File Logger
10631
#pragma warning restore SA1401 // Fields should be private
10732

@@ -209,89 +134,22 @@ private string GenerateLogStr(EventWrittenEventArgs eventData)
209134
json.Add(keys[i], JToken.FromObject(values[i]));
210135
}
211136

212-
// These are for ease of use in our linux-dedicated regex-compensation strategy
213-
string activityId = "";
214-
string relatedActivityId = "";
215-
216137
// Add ActivityId and RelatedActivityId, if non-null
217138
if (!eventData.ActivityId.Equals(Guid.Empty))
218139
{
219140
json.Add("ActivityId", eventData.ActivityId);
220-
activityId = eventData.ActivityId.ToString();
221141
}
222142

223143
if (!eventData.RelatedActivityId.Equals(Guid.Empty))
224144
{
225145
json.Add("RelatedActivityId", eventData.RelatedActivityId);
226-
relatedActivityId = eventData.RelatedActivityId.ToString();
227-
}
228-
229-
string logString;
230-
if (!this.writeToConsole)
231-
{
232-
// This path supports the legacy regex-based parser in Linux Dedicated.
233-
// In the future, we'll be able to remove this and use JSON logging only
234-
List<string> regexValues = new List<string>();
235-
236-
foreach (string column in this.orderedRegexCol)
237-
{
238-
string val = "";
239-
if (json.TryGetValue(column, out JToken valJToken))
240-
{
241-
// We escape a few special characters to avoid parsing problems:
242-
// (1) Escaping newline-like characters such as \n and \r to keep logs being 1 line
243-
// (2) Escaping double-quotes (") to be single-quotes (') because some fields in our regex string are
244-
// deliniated by double-quotes. Note, this was a convention copied from the Functions Host regex
245-
// which also uses double-quotes to capture columns that may contain commas inside them.
246-
// (3) Escaping commas (",") for ";;" because commas separate our columns and so they have the potential to
247-
// disrupt parsing.
248-
// Note: In retrospective, perhaps the regex-string could have been designed to avoid these awkward
249-
// parsing problems, but it wasn't because (1) it followed conventions from other regex-strings in our system
250-
// and because we asssumed we'd have a JSON-based logger that would have avoided these problems.
251-
val = (string)valJToken;
252-
val = val.Replace("\n", "\\n").Replace("\r", "\\r").Replace("\"", "'").Replace(",", ";;");
253-
}
254-
255-
if (column == "Details" || column == "ExceptionMessage")
256-
{
257-
// Since Details and Exceptions may include commas, our regex string
258-
// expects this field to be wrapped in double-quotes to avoid capturing an "inner comma",
259-
// a convention adopted by the Functions Host regex string.
260-
val = '"' + val + '"';
261-
}
262-
263-
regexValues.Add(val);
264-
}
265-
266-
logString = string.Join(",", regexValues);
267-
268-
// To compensate for the missing columns in our regex, we write extra columns to WorkerName while separated
269-
// with a special delineator: ";;DURABLEFUNCTIONS;;"
270-
string delineator = ";;DURABLEFUNCTIONS;;";
271-
string[] extraCols =
272-
{
273-
(string)json["TaskName"],
274-
(string)json["EventId"],
275-
(string)json["ProviderName"],
276-
(string)json["Level"],
277-
(string)json["Pid"],
278-
(string)json["Tid"],
279-
(string)json["EventTimestamp"],
280-
activityId,
281-
relatedActivityId,
282-
};
283-
284-
logString += delineator + string.Join(delineator, extraCols);
285-
}
286-
else
287-
{
288-
// Generate string-representation of JSON.
289-
// Newtonsoft should take care of removing newlines for us.
290-
// It is also important to specify no formatting to avoid
291-
// pretty printing.
292-
logString = json.ToString(Newtonsoft.Json.Formatting.None);
293146
}
294147

148+
// Generate string-representation of JSON.
149+
// Newtonsoft should take care of removing newlines for us.
150+
// It is also important to specify no formatting to avoid
151+
// pretty printing.
152+
string logString = json.ToString(Newtonsoft.Json.Formatting.None);
295153
return logString;
296154
}
297155

0 commit comments

Comments
 (0)