Skip to content

Commit

Permalink
Add support to upload all module logs with one command (#1026)
Browse files Browse the repository at this point in the history
* Upload all support

* Add const for all

* Fix LogsUploadRequestHandler registration

* Fix null check

* Add tests

* Add tests

* Fix stylecop issues

* Fix build
  • Loading branch information
varunpuranik authored Apr 3, 2019
1 parent 951afd8 commit edaad81
Show file tree
Hide file tree
Showing 8 changed files with 196 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ public static class Constants

public const string RequestTimeoutSecs = "RequestTimeoutSecs";

public const string AllModulesIdentifier = "all";

public static class Labels
{
public const string Version = "net.azure-devices.edge.version";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,22 @@
namespace Microsoft.Azure.Devices.Edge.Agent.Core.Logs
{
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Microsoft.Azure.Devices.Edge.Util;
using Microsoft.Azure.Devices.Edge.Util.Json;
using Newtonsoft.Json;

public class ModuleLogFilter
public class ModuleLogFilter : IEquatable<ModuleLogFilter>
{
public static ModuleLogFilter Empty = new ModuleLogFilter(Option.None<int>(), Option.None<int>(), Option.None<int>(), Option.None<string>());

public ModuleLogFilter(Option<int> tail, Option<int> since, Option<int> logLevel, Option<string> regex)
{
this.Tail = tail;
this.Since = since;
this.LogLevel = logLevel;
this.RegexString = regex;
this.Regex = regex.Map(r => new Regex(r));
}

Expand All @@ -23,8 +27,6 @@ public ModuleLogFilter(Option<int> tail, Option<int> since, Option<int> logLevel
{
}

public static ModuleLogFilter Empty = new ModuleLogFilter(Option.None<int>(), Option.None<int>(), Option.None<int>(), Option.None<string>());

[JsonProperty("tail")]
[JsonConverter(typeof(OptionConverter<int>), true)]
public Option<int> Tail { get; }
Expand All @@ -37,8 +39,31 @@ public ModuleLogFilter(Option<int> tail, Option<int> since, Option<int> logLevel
[JsonConverter(typeof(OptionConverter<int>), true)]
public Option<int> LogLevel { get; }

[JsonProperty("regex")]
[JsonConverter(typeof(OptionConverter<Regex>))]
[JsonIgnore]
public Option<Regex> Regex { get; }

[JsonProperty("regex")]
[JsonConverter(typeof(OptionConverter<string>))]
public Option<string> RegexString { get; }

public override bool Equals(object obj)
=> this.Equals(obj as ModuleLogFilter);

public bool Equals(ModuleLogFilter other)
=> other != null &&
this.Tail.Equals(other.Tail) &&
this.Since.Equals(other.Since) &&
this.LogLevel.Equals(other.LogLevel) &&
this.RegexString.Equals(other.RegexString);

public override int GetHashCode()
{
var hashCode = -132418255;
hashCode = hashCode * -1521134295 + EqualityComparer<Option<int>>.Default.GetHashCode(this.Tail);
hashCode = hashCode * -1521134295 + EqualityComparer<Option<int>>.Default.GetHashCode(this.Since);
hashCode = hashCode * -1521134295 + EqualityComparer<Option<int>>.Default.GetHashCode(this.LogLevel);
hashCode = hashCode * -1521134295 + EqualityComparer<Option<string>>.Default.GetHashCode(this.RegexString);
return hashCode;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public ModuleLogMessageData(
string fullText)
: base(iotHub, deviceId, moduleId, stream, logLevel, timeStamp, text)
{
this.FullText = Preconditions.CheckNonWhiteSpace(fullText, fullText);
this.FullText = Preconditions.CheckNotNull(fullText, fullText);
this.FullFrame = Preconditions.CheckNotNull(fullFrame, nameof(fullFrame));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// Copyright (c) Microsoft. All rights reserved.
namespace Microsoft.Azure.Devices.Edge.Agent.Core.Logs
{
using System;
using System.Collections.Generic;
using Microsoft.Azure.Devices.Edge.Util;

public class ModuleLogOptions
public class ModuleLogOptions : IEquatable<ModuleLogOptions>
{
public ModuleLogOptions(string id, LogsContentEncoding contentEncoding, LogsContentType contentType, ModuleLogFilter filter)
{
Expand All @@ -14,8 +16,31 @@ public ModuleLogOptions(string id, LogsContentEncoding contentEncoding, LogsCont
}

public string Id { get; }

public LogsContentEncoding ContentEncoding { get; }

public LogsContentType ContentType { get; }

public ModuleLogFilter Filter { get; }

public override bool Equals(object obj)
=> this.Equals(obj as ModuleLogOptions);

public bool Equals(ModuleLogOptions other)
=> other != null &&
this.Id == other.Id &&
this.ContentEncoding == other.ContentEncoding &&
this.ContentType == other.ContentType &&
EqualityComparer<ModuleLogFilter>.Default.Equals(this.Filter, other.Filter);

public override int GetHashCode()
{
var hashCode = -1683996196;
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(this.Id);
hashCode = hashCode * -1521134295 + this.ContentEncoding.GetHashCode();
hashCode = hashCode * -1521134295 + this.ContentType.GetHashCode();
hashCode = hashCode * -1521134295 + EqualityComparer<ModuleLogFilter>.Default.GetHashCode(this.Filter);
return hashCode;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,39 @@ namespace Microsoft.Azure.Devices.Edge.Agent.Core.Requests

public class LogsUploadRequest
{
public LogsUploadRequest(string id, LogsContentEncoding encoding, LogsContentType contentType, string sasUrl)
public LogsUploadRequest(
string id,
LogsContentEncoding encoding,
LogsContentType contentType,
string sasUrl,
ModuleLogFilter filter)
{
this.Id = Preconditions.CheckNonWhiteSpace(id, nameof(id));
this.Encoding = encoding;
this.ContentType = contentType;
this.SasUrl = sasUrl;
this.Filter = filter ?? ModuleLogFilter.Empty;
}

[JsonConstructor]
LogsUploadRequest(string id, LogsContentEncoding? encoding, LogsContentType? contentType, string sasUrl)
: this(id, encoding.HasValue ? encoding.Value : LogsContentEncoding.None, contentType.HasValue ? contentType.Value : LogsContentType.Json, sasUrl)
LogsUploadRequest(
string id,
LogsContentEncoding? encoding,
LogsContentType? contentType,
string sasUrl,
ModuleLogFilter filter)
: this(id, encoding ?? LogsContentEncoding.None, contentType ?? LogsContentType.Json, sasUrl, filter)
{
}

public string Id { get; }

public LogsContentEncoding Encoding { get; }

public LogsContentType ContentType { get; }

public string SasUrl { get; }

public ModuleLogFilter Filter { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
namespace Microsoft.Azure.Devices.Edge.Agent.Core.Requests
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.Devices.Edge.Agent.Core.Logs;
Expand All @@ -11,22 +13,44 @@ public class LogsUploadRequestHandler : RequestHandlerBase<LogsUploadRequest, ob
{
readonly ILogsUploader logsUploader;
readonly ILogsProvider logsProvider;
readonly IRuntimeInfoProvider runtimeInfoProvider;

public LogsUploadRequestHandler(ILogsUploader logsUploader, ILogsProvider logsProvider)
public LogsUploadRequestHandler(ILogsUploader logsUploader, ILogsProvider logsProvider, IRuntimeInfoProvider runtimeInfoProvider)
{
this.logsProvider = Preconditions.CheckNotNull(logsProvider, nameof(logsProvider));
this.logsUploader = Preconditions.CheckNotNull(logsUploader, nameof(logsUploader));
this.runtimeInfoProvider = Preconditions.CheckNotNull(runtimeInfoProvider, nameof(runtimeInfoProvider));
}

public override string RequestName => "UploadLogs";

protected override async Task<Option<object>> HandleRequestInternal(Option<LogsUploadRequest> payloadOption, CancellationToken cancellationToken)
{
LogsUploadRequest payload = payloadOption.Expect(() => new ArgumentException("Request payload not found"));
var moduleLogOptions = new ModuleLogOptions(payload.Id, payload.Encoding, payload.ContentType, ModuleLogFilter.Empty);
byte[] logBytes = await this.logsProvider.GetLogs(moduleLogOptions, cancellationToken);
await this.logsUploader.Upload(payload.SasUrl, payload.Id, logBytes, payload.Encoding, payload.ContentType);

async Task<IList<string>> GetModuleIds()
{
if (payload.Id == Constants.AllModulesIdentifier)
{
IEnumerable<ModuleRuntimeInfo> modules = await this.runtimeInfoProvider.GetModules(cancellationToken);
return modules.Select(m => m.Name).ToList();
}
else
{
return new List<string> { payload.Id };
}
}

IList<string> moduleIds = await GetModuleIds();
IEnumerable<Task> uploadTasks = moduleIds.Select(m => this.UploadLogs(payload.SasUrl, new ModuleLogOptions(m, payload.Encoding, payload.ContentType, payload.Filter), cancellationToken));
await Task.WhenAll(uploadTasks);
return Option.None<object>();
}

async Task UploadLogs(string sasUrl, ModuleLogOptions moduleLogOptions, CancellationToken token)
{
byte[] logBytes = await this.logsProvider.GetLogs(moduleLogOptions, token);
await this.logsUploader.Upload(sasUrl, moduleLogOptions.Id, logBytes, moduleLogOptions.ContentEncoding, moduleLogOptions.ContentType);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,14 @@ protected override void Load(ContainerBuilder builder)
async c =>
{
var logsUploader = c.Resolve<ILogsUploader>();
ILogsProvider logsProvider = await c.Resolve<Task<ILogsProvider>>();
var runtimeInfoProviderTask = c.Resolve<Task<IRuntimeInfoProvider>>();
var logsProviderTask = c.Resolve<Task<ILogsProvider>>();
IRuntimeInfoProvider runtimeInfoProvider = await runtimeInfoProviderTask;
ILogsProvider logsProvider = await logsProviderTask;
var requestHandlers = new List<IRequestHandler>
{
new PingRequestHandler(),
new LogsUploadRequestHandler(logsUploader, logsProvider)
new LogsUploadRequestHandler(logsUploader, logsProvider, runtimeInfoProvider)
};
return new RequestManager(requestHandlers, this.requestTimeout) as IRequestManager;
})
Expand Down
Loading

0 comments on commit edaad81

Please sign in to comment.