Skip to content

Commit e662e79

Browse files
authored
Merge pull request #1530 from Azure/dev
Promote to master for 2.3.1 release - 3rd instance
2 parents 9d28262 + 436a381 commit e662e79

13 files changed

+957
-316
lines changed

src/WebJobs.Extensions.DurableTask.Analyzers/SyntaxNodeUtils.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public static bool TryGetISymbol(SemanticModel semanticModel, SyntaxNode node, o
5757
{
5858
if (node != null && TryGetSemanticModelForSyntaxTree(semanticModel, node, out SemanticModel newModel))
5959
{
60-
symbol = semanticModel.GetSymbolInfo(node).Symbol;
60+
symbol = newModel.GetSymbolInfo(node).Symbol;
6161
return symbol != null;
6262
}
6363

src/WebJobs.Extensions.DurableTask.Analyzers/WebJobs.Extensions.DurableTask.Analyzers.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
<PropertyGroup>
1010
<PackageId>Microsoft.Azure.WebJobs.Extensions.DurableTask.Analyzers</PackageId>
11-
<PackageVersion>0.3.1</PackageVersion>
11+
<PackageVersion>0.3.2</PackageVersion>
1212
<Authors>Microsoft</Authors>
1313
<PackageLicenseUrl>https://go.microsoft.com/fwlink/?linkid=2028464</PackageLicenseUrl>
1414
<PackageProjectUrl>https://github.com/Azure/azure-functions-durable-extension</PackageProjectUrl>

src/WebJobs.Extensions.DurableTask/DurableTaskExtension.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,8 +221,11 @@ private MessagePayloadDataConverter CreateErrorDataConverter(IErrorSerializerSet
221221
/// <param name="context">Extension context provided by WebJobs.</param>
222222
void IExtensionConfigProvider.Initialize(ExtensionConfigContext context)
223223
{
224+
#if !FUNCTIONS_V1
225+
// .NET461 is not supported in linux, so this is conditionally compiled
224226
// We initialize linux logging early on in case any initialization steps below were to trigger a log event.
225227
this.InitializeLinuxLogging();
228+
#endif
226229

227230
ConfigureLoaderHooks();
228231

@@ -342,15 +345,21 @@ private void InitializeLinuxLogging()
342345
// The logging service for linux works by capturing EventSource messages,
343346
// which our linux platform does not recognize, and logging them via a
344347
// different strategy such as writing to console or to a file.
345-
this.eventSourceListener = new EventSourceListener(linuxLogger);
348+
349+
// Since our logging payload can be quite large, linux telemetry by default
350+
// disables verbose-level telemetry to avoid a performance hit.
351+
bool enableVerbose = this.Options.Tracing.AllowVerboseLinuxTelemetry;
352+
this.eventSourceListener = new EventSourceListener(linuxLogger, enableVerbose);
346353
}
347354
}
348355

349356
/// <inheritdoc />
350357
public void Dispose()
351358
{
352359
// Not flushing the linux logger may lead to lost logs
353-
Serilog.Log.CloseAndFlush();
360+
// 40 seconds timeout because we write normally every 30 seconds, so we're just
361+
// adding an extra 10 seconds to flush.
362+
LinuxAppServiceLogger.Logger?.Stop(TimeSpan.FromSeconds(40));
354363
this.HttpApiHandler?.Dispose();
355364
this.eventSourceListener?.Dispose();
356365
}

src/WebJobs.Extensions.DurableTask/EndToEndTraceHelper.cs

Lines changed: 224 additions & 240 deletions
Large diffs are not rendered by default.

src/WebJobs.Extensions.DurableTask/EventSourceListener.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,18 @@ namespace Microsoft.Azure.WebJobs.Extensions.DurableTask
1414
internal class EventSourceListener : EventListener
1515
{
1616
private readonly LinuxAppServiceLogger logger;
17+
private readonly bool disableVerbose;
1718

1819
/// <summary>
1920
/// Create an EventSourceListener to capture and log Durable EventSource
2021
/// data in Linux.
2122
/// </summary>
2223
/// <param name="logger">A LinuxAppService logger configured for the current linux host.</param>
23-
public EventSourceListener(LinuxAppServiceLogger logger)
24+
/// <param name="enableVerbose">If true, durableTask.Core verbose logs are enabled. The opposite if false.</param>
25+
public EventSourceListener(LinuxAppServiceLogger logger, bool enableVerbose)
2426
{
2527
this.logger = logger;
28+
this.disableVerbose = !enableVerbose; // We set the opposite to simply logic later
2629
}
2730

2831
/// <summary>
@@ -52,7 +55,13 @@ protected override void OnEventSourceCreated(EventSource eventSource)
5255
/// <param name="eventData">The EventSource event data, for logging.</param>
5356
protected override void OnEventWritten(EventWrittenEventArgs eventData)
5457
{
55-
this.logger.Log(eventData);
58+
// When disabling verbose logs, we skip Verbose DurableTask-Core telemetry
59+
if (!(this.disableVerbose
60+
&& eventData.EventSource.Name == "DurableTask-Core"
61+
&& eventData.Level == EventLevel.Verbose))
62+
{
63+
this.logger.Log(eventData);
64+
}
5665
}
5766
}
5867
}
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See LICENSE in the project root for license information.
3+
4+
using System;
5+
using System.Collections.Concurrent;
6+
using System.Collections.Generic;
7+
using System.IO;
8+
using System.Linq;
9+
using System.Threading;
10+
using System.Threading.Tasks;
11+
12+
#if !FUNCTIONS_V1
13+
using Mono.Unix.Native;
14+
#endif
15+
16+
namespace Microsoft.Azure.WebJobs.Extensions.DurableTask
17+
{
18+
/// <summary>
19+
/// The File logger for linux dedicated. Manages file rolling and is concurrency-safe.
20+
/// This is copied over from the azure-funtions-host codebase here:
21+
/// https://github.com/Azure/azure-functions-host/blob/35cf323fa3464a08b410a518bcab006e801301fe/src/WebJobs.Script.WebHost/Diagnostics/LinuxAppServiceFileLogger.cs
22+
/// We have modified their implementation to utilize syscall.rename instead of File.Move during file rolling.
23+
/// This change is necessary for older versions of fluent-bit, our logging infrastructure in linux dedicated, to properly deal with logfile archiving.
24+
/// </summary>
25+
public class LinuxAppServiceFileLogger
26+
{
27+
private readonly string logFileName;
28+
private readonly string logFileDirectory;
29+
private readonly string logFilePath;
30+
private readonly string archiveFilePath;
31+
private readonly BlockingCollection<string> buffer;
32+
private readonly List<string> currentBatch;
33+
private readonly CancellationTokenSource cancellationTokenSource;
34+
private Task outputTask;
35+
36+
/// <summary>
37+
/// The File logger for linux dedicated. Manages file rolling and is concurrency-safe.
38+
/// </summary>
39+
/// <param name="logFileName">Name of target logfile.</param>
40+
/// <param name="logFileDirectory">Directory of target logfile.</param>
41+
/// <param name="startOnCreate">Whether or not to start monitoring the write buffer at initialization time.</param>
42+
public LinuxAppServiceFileLogger(string logFileName, string logFileDirectory, bool startOnCreate = true)
43+
{
44+
this.logFileName = logFileName;
45+
this.logFileDirectory = logFileDirectory;
46+
this.logFilePath = Path.Combine(this.logFileDirectory, this.logFileName);
47+
this.archiveFilePath = this.logFilePath + "1";
48+
this.buffer = new BlockingCollection<string>(new ConcurrentQueue<string>());
49+
this.currentBatch = new List<string>();
50+
this.cancellationTokenSource = new CancellationTokenSource();
51+
52+
if (startOnCreate)
53+
{
54+
this.Start();
55+
}
56+
}
57+
58+
// Maximum size of individual log file in MB
59+
private int MaxFileSizeMb { get; set; } = 10;
60+
61+
// Maximum time between successive flushes (seconds)
62+
private int FlushFrequencySeconds { get; set; } = 30;
63+
64+
/// <summary>
65+
/// Log a string.
66+
/// </summary>
67+
/// <param name="message">Message to log.</param>
68+
public virtual void Log(string message)
69+
{
70+
try
71+
{
72+
this.buffer.Add(message);
73+
}
74+
catch (Exception)
75+
{
76+
// ignored
77+
}
78+
}
79+
80+
private void Start()
81+
{
82+
if (this.outputTask == null)
83+
{
84+
this.outputTask = Task.Factory.StartNew(this.ProcessLogQueue, null, TaskCreationOptions.LongRunning);
85+
}
86+
}
87+
88+
/// <summary>
89+
/// Flushes the write buffer, stops writing to logfile afterwards.
90+
/// </summary>
91+
/// <param name="timeSpan">Timeout in milliseconds for flushing task.</param>
92+
public void Stop(TimeSpan timeSpan)
93+
{
94+
this.cancellationTokenSource.Cancel();
95+
96+
try
97+
{
98+
this.outputTask?.Wait(timeSpan);
99+
}
100+
catch (Exception)
101+
{
102+
// ignored
103+
}
104+
}
105+
106+
private async Task ProcessLogQueue(object state)
107+
{
108+
while (!this.cancellationTokenSource.IsCancellationRequested)
109+
{
110+
await this.InternalProcessLogQueue();
111+
await Task.Delay(TimeSpan.FromSeconds(this.FlushFrequencySeconds), this.cancellationTokenSource.Token).ContinueWith(task => { });
112+
}
113+
114+
await this.InternalProcessLogQueue();
115+
116+
// ReSharper disable once FunctionNeverReturns
117+
}
118+
119+
// internal for unittests (in func host)
120+
internal async Task InternalProcessLogQueue()
121+
{
122+
string currentMessage;
123+
while (this.buffer.TryTake(out currentMessage))
124+
{
125+
this.currentBatch.Add(currentMessage);
126+
}
127+
128+
if (this.currentBatch.Any())
129+
{
130+
try
131+
{
132+
await this.WriteLogs(this.currentBatch);
133+
}
134+
catch (Exception)
135+
{
136+
// Ignored
137+
}
138+
139+
this.currentBatch.Clear();
140+
}
141+
}
142+
143+
private async Task WriteLogs(IEnumerable<string> currentBatch)
144+
{
145+
// If the directory already exists, this does nothing
146+
Directory.CreateDirectory(this.logFileDirectory);
147+
148+
var fileInfo = new FileInfo(this.logFilePath);
149+
if (fileInfo.Exists)
150+
{
151+
if (fileInfo.Length / (1024 * 1024) >= this.MaxFileSizeMb)
152+
{
153+
this.RollFiles();
154+
}
155+
}
156+
157+
await this.AppendLogs(this.logFilePath, currentBatch);
158+
}
159+
160+
private async Task AppendLogs(string filePath, IEnumerable<string> logs)
161+
{
162+
using (var streamWriter = File.AppendText(filePath))
163+
{
164+
foreach (var log in logs)
165+
{
166+
await streamWriter.WriteLineAsync(log);
167+
}
168+
}
169+
}
170+
171+
private void RollFiles()
172+
{
173+
// Rename current file to older file.
174+
175+
#if !FUNCTIONS_V1
176+
Syscall.rename(this.logFilePath, this.archiveFilePath);
177+
#endif
178+
179+
}
180+
}
181+
}

0 commit comments

Comments
 (0)