Skip to content

Commit

Permalink
RavenDB-20503 Persist log configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
haludi committed Dec 18, 2023
1 parent 0d3ca12 commit 7a88dba
Show file tree
Hide file tree
Showing 7 changed files with 453 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ namespace Raven.Client.ServerWide.Operations.Logs
public class SetLogsConfigurationOperation : IServerOperation
{
private readonly Parameters _parameters;
private readonly bool _persist;

public class Parameters
{
Expand All @@ -34,27 +35,35 @@ public Parameters(GetLogsConfigurationResult currentLogsConfiguration)
}

public SetLogsConfigurationOperation(Parameters parameters)
: this(parameters, false)
{
}

public SetLogsConfigurationOperation(Parameters parameters, bool persist)
{
_parameters = parameters ?? throw new ArgumentNullException(nameof(parameters));
_persist = persist;
}

public RavenCommand GetCommand(DocumentConventions conventions, JsonOperationContext context)
{
return new SetLogsConfigurationCommand(_parameters);
return new SetLogsConfigurationCommand(_parameters, _persist);
}

private class SetLogsConfigurationCommand : RavenCommand
{
private readonly Parameters _parameters;
private readonly bool? _persist;

public SetLogsConfigurationCommand(Parameters parameters)
public SetLogsConfigurationCommand(Parameters parameters, bool persist)
{
_parameters = parameters ?? throw new ArgumentNullException(nameof(parameters));
_persist = persist;
}

public override HttpRequestMessage CreateRequest(JsonOperationContext ctx, ServerNode node, out string url)
{
url = $"{node.Url}/admin/logs/configuration";
url = $"{node.Url}/admin/logs/configuration?persist={_persist}";

return new HttpRequestMessage(HttpMethod.Post, url)
{
Expand Down
7 changes: 5 additions & 2 deletions src/Raven.Server/Config/Categories/ConfigurationCategory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -298,11 +298,14 @@ public void Initialize(Func<string, SettingValue> getSetting, string serverDataD
{
property.Info.SetValue(this, Math.Max(Convert.ToInt32(value), minValue.Int32Value));
}
else if (property.Info.PropertyType == typeof(Size) ||
property.Info.PropertyType == typeof(Size?))
else if (property.Info.PropertyType == typeof(Size))
{
property.Info.SetValue(this, new Size(Math.Max(Convert.ToInt32(value), minValue.Int32Value), sizeUnit.Unit));
}
else if (property.Info.PropertyType == typeof(Size?))
{
property.Info.SetValue(this, string.IsNullOrEmpty(value) ? null : new Size(Math.Max(Convert.ToInt32(value), minValue.Int32Value), sizeUnit.Unit));
}
else if (property.Info.PropertyType == typeof(TimeSetting) ||
property.Info.PropertyType == typeof(TimeSetting?))
{
Expand Down
140 changes: 140 additions & 0 deletions src/Raven.Server/Config/JsonFileModifier.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
using System;
using System.IO;
using Newtonsoft.Json;
using Raven.Client.Exceptions;
using Raven.Client.Exceptions.Security;
using Raven.Client.Json.Serialization.NewtonsoftJson.Internal;
using Sparrow.Json;
using Sparrow.Json.Parsing;
using Sparrow.Platform;
using Sparrow.Server.Json.Sync;
using Sparrow.Server.Platform.Posix;
using Sparrow.Utils;

namespace Raven.Server.Config;

public class JsonFileModifier
{
private readonly string _path;
private readonly bool _reset;
private readonly DynamicJsonValue _modifications = new DynamicJsonValue();

public JsonFileModifier(string path, bool reset = false)
{
_path = path;
_reset = reset;
}

public object this[string key]
{
set
{
_modifications[key] = value;
}
}

public void Execute(JsonOperationContext context)
{
using var json = ReadBlittableFromFile(context);
json.Modifications = _modifications;
var modifiedJsonObj = context.ReadObject(json, "modified-settings-json");
PersistConfiguration(modifiedJsonObj);
}

private BlittableJsonReaderObject ReadBlittableFromFile(JsonOperationContext context)
{
var fileName = Path.GetFileName(_path);
if(_reset)
return GetEmptyBlittable(context, fileName);
try
{
using var fs = OpenFile(_path, FileMode.Open, FileAccess.ReadWrite);
return context.Sync.ReadForMemory(fs, fileName);
}
catch (FileNotFoundException)
{
return GetEmptyBlittable(context, fileName);
}
}

private static BlittableJsonReaderObject GetEmptyBlittable(JsonOperationContext context, string docId)
=> context.ReadObject(new DynamicJsonValue(), docId);

private static FileStream OpenFile(string path, FileMode fileMode, FileAccess fileAccess)
{
try
{
return SafeFileStream.Create(path, fileMode, fileAccess);
}
catch (Exception e) when (e is UnauthorizedAccessException or SecurityException)
{
throw new UnsuccessfulFileAccessException(e, path, fileAccess);
}
}

private void PersistConfiguration(BlittableJsonReaderObject json)
{
using var tempFile = new TempFile(_path);

using (var file = OpenFile(tempFile.Path, FileMode.Create, FileAccess.ReadWrite))
using (var streamWriter = new StreamWriter(file))
using (var writer = new JsonTextWriter(streamWriter))
using (var reader = new BlittableJsonReader())
{
writer.Formatting = Formatting.Indented;
reader.Initialize(json);

writer.WriteToken(reader);

writer.Flush();
streamWriter.Flush();
file.Flush(true);
}

SwitchTempWithOriginalAndCreateBackup(tempFile.Path);
}

private void SwitchTempWithOriginalAndCreateBackup(string tempPath)
{
try
{
if (File.Exists(_path))
{
File.Replace(tempPath, _path, _path + ".bak");
}
else
{
File.Move(tempPath, _path);
}

if (PlatformDetails.RunningOnPosix)
Syscall.FsyncDirectoryFor(_path);
}
catch (Exception e) when (e is UnauthorizedAccessException or SecurityException)
{
throw new UnsuccessfulFileAccessException(e, _path, FileAccess.Write);
}
}

private class TempFile : IDisposable
{
public TempFile(string path)
{
Path = path + ".tmp";
}

public string Path { get; }

public void Dispose()
{
try
{
File.Delete(Path);
}
catch
{
// ignored
}
}
}
}
12 changes: 12 additions & 0 deletions src/Raven.Server/Config/PersistConfigurationException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;

namespace Raven.Server.Config;

public class PersistConfigurationException : Exception
{
public PersistConfigurationException(string message, Exception exception)
: base(message, exception)
{

}
}
44 changes: 44 additions & 0 deletions src/Raven.Server/Documents/Handlers/Admin/AdminLogsHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.IO.Compression;
using System.Threading.Tasks;
using Raven.Client.ServerWide.Operations.Logs;
using Raven.Server.Config;
using Raven.Server.Utils.MicrosoftLogging;
using Raven.Server.Json;
using Raven.Server.Routing;
Expand Down Expand Up @@ -60,6 +61,26 @@ public async Task SetConfiguration()
configuration.RetentionTime,
configuration.RetentionSize?.GetValue(SizeUnit.Bytes),
configuration.Compress);

if (GetBoolValueQueryString("persist", false) == true)
{
try
{
var jsonFileModifier = new JsonFileModifier(ServerStore.Configuration.ConfigPath)
{
[RavenConfiguration.GetKey(x => x.Logs.Mode)] = LoggingSource.Instance.LogMode,
[RavenConfiguration.GetKey(x => x.Logs.RetentionSize)] = LoggingSource.Instance.RetentionSize == long.MaxValue
? null : new Size(LoggingSource.Instance.RetentionSize, SizeUnit.Bytes).GetValue(SizeUnit.Megabytes),
[RavenConfiguration.GetKey(x => x.Logs.RetentionTime)] = (int)LoggingSource.Instance.RetentionTime.TotalHours,
[RavenConfiguration.GetKey(x => x.Logs.Compress)] = LoggingSource.Instance.Compressing
};
jsonFileModifier.Execute(context);
}
catch (Exception e)
{
throw new PersistConfigurationException("The log configuration was modified but couldn't be persistent. The configuration will be reverted on server restart.", e);
}
}
}

NoContentStatus();
Expand Down Expand Up @@ -213,6 +234,29 @@ public async Task SetMicrosoftConfiguration()
var provider = Server.GetService<MicrosoftLoggingProvider>();
await provider.Configuration.ReadConfigurationAsync(RequestBodyStream(), context, reset);
provider.ApplyConfiguration();

if (GetBoolValueQueryString("persist", required: false) == true)
{
try
{
var microsoftConfigModifier = new JsonFileModifier(ServerStore.Configuration.Logs.MicrosoftLogsConfigurationPath.FullPath, true);
foreach (var (category, logLevel) in Server.GetService<MicrosoftLoggingProvider>().Configuration)
{
microsoftConfigModifier[category] = logLevel;
}
microsoftConfigModifier.Execute(context);

var settingJsonConfigModifier = new JsonFileModifier(ServerStore.Configuration.ConfigPath)
{
[RavenConfiguration.GetKey(x => x.Logs.DisableMicrosoftLogs)] = false
};
settingJsonConfigModifier.Execute(context);
}
catch (Exception e)
{
throw new PersistConfigurationException("The microsoft configuration was modified but couldn't be persistent. The configuration will be reverted on server restart.", e);
}
}
}

NoContentStatus();
Expand Down
Loading

0 comments on commit 7a88dba

Please sign in to comment.