웹 앱용 CloudWatch 로깅(3부)
13798 단어 cloudwatchblazorawsserverless
개요
프런트엔드 클라이언트는 각 앱 세션에 대한 CloudWatch 로그 스트림을 생성한 다음 순차적 배치로 로그 메시지를 작성하는 역할을 합니다. 첫 번째 배치가 아닌 경우 각 배치에는 이전 배치의 시퀀스 번호가 포함되어야 합니다.
LambdaSharp로 빌드된 Blazor WebAssembly 앱의 경우 클라이언트 구현은 LambdaSharpAppClient 클래스에 있습니다. 클래스는 싱글톤으로 인스턴스화되며 ILogger
인터페이스를 통해 직접 또는 간접적으로 주입할 수 있습니다. 프런트엔드 앱이 CloudWatch에 기록하려면 다음 문 중 하나만 필요합니다. 선택은 개인 취향에 달려 있습니다.
@inject LambdaSharpAppClient AppClient
@inject ILogger<Index> Logger
구현
CloudWatch Logs는 로그 항목을 로그 스트림으로 구성합니다. 로그 스트림은 항목의 시간순입니다. 많은 로그 스트림이 동시에 존재할 수 있습니다. Blazor WebAssembly 앱 및 기타 단일 페이지 앱의 경우 첫 번째 로그 메시지가 생성될 때 요청 시 로그 스트림을 만드는 것이 좋습니다.
이 게시물의 코드는 단순화를 위해 수정되었습니다. 실제 구현에서는 주의를 산만하게 만드는 몇 가지 예외적인 경우를 더 다룹니다. 완전한 구현을 찾을 수 있습니다here.
로그 메시지 보내기
앱 클라이언트는 내부 누적기에 메시지를 큐에 넣습니다. 이를 통해 구현이 제한을 피하기 위해 한 번에 여러 메시지를 보낼 수 있습니다.
private readonly List<PutLogEventsRequestEntry> _logs = new List<PutLogEventsRequestEntry>();
private void SendMessage(string message) {
// queue message for server-side logging
_logs.Add(new PutLogEventsRequestEntry {
Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
Message = message ?? throw new ArgumentNullException(nameof(message))
});
}
시간 누적기
누산기는 대기 중인 메시지에 대한 타이머에 의해 매초 확인됩니다. 타이머 콜백은 먼저 이전 비동기 작업이 완료되었는지 확인합니다. 그런 다음 누적된 메시지를 플러시하려고 시도합니다.
private Task _previousOperationTask;
private void OnTimer(object _) {
if(!(_previousOperationTask?.IsCompleted ?? true)) {
// previous operation is still going; wait until next timer invocation to proceed
return;
}
// initialize invocation to FlushAsync(), but don't wait for it to finish
_previousOperationTask = FlushAsync();
}
일괄 전송
누적된 메시지의 첫 번째 배치를 보내기 전에 클라이언트는 로그 스트림이 생성되었는지 확인해야 합니다. 그런 다음 누적된 메시지를 1MB 크기 또는 10,000개 메시지 중 더 작은 것으로 제한된 배치로 청크합니다. 클라이언트가 오프라인일 가능성이 있기 때문에 작업이 실패하면 메시지가 누적기에 다시 삽입됩니다.
private string _logStreamName;
private string _sequenceToken;
private async Task FlushAsync() {
// check if any messages are pending
if(!_logs.Any()) {
return;
}
// check if a log stream must be created
if(_logStreamName == null) {
_logStreamName = AppInstanceId;
var response = await CreateLogStreamAsync(new CreateLogStreamRequest {
LogStreamName = _logStreamName
});
if(response.Error != null) {
Console.WriteLine($"*** ERROR: unable to create log stream: {_logStreamName} (Error: {response.Error})");
return;
}
}
// NOTE (2020-08-06, bjorg): we limit the number of log message we send in the unlikely event that we have too many
// See: https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_PutLogEvents.html
const int MaxPayloadSize = 1_048_576;
const int MaxMessageCount = 10_000;
// consume as many accumulated log messages as possible
var payloadByteCount = 0;
var logs = _logs.Take(MaxMessageCount).TakeWhile(log => {
var logMessageByteCount = Encoding.UTF8.GetByteCount(log.Message) + 26;
if((payloadByteCount + logMessageByteCount) >= MaxPayloadSize) {
return false;
}
payloadByteCount += logMessageByteCount;
return true;
}).ToList();
_logs.RemoveRange(0, logs.Count);
// send log messages to CloudWatch Logs
try {
var response = await PutLogEventsAsync(new PutLogEventsRequest {
LogStreamName = _logStreamName,
LogEvents = logs,
SequenceToken = _sequenceToken
});
// on error, re-insert the log messages and try again later
if(response.Error != null) {
_logs.InsertRange(0, logs);
return;
}
_sequenceToken = response.NextSequenceToken;
} catch {
// on exception, re-insert the log messages and try again later
_logs.InsertRange(0, logs);
}
}
처분
마지막으로, 클라이언트는 누적기에서 보류 중인 모든 메시지가 CloudWatch로 전송되도록 하기 위해 폐기될 때 최종 플러시 작업을 수행합니다.
async ValueTask IAsyncDisposable.DisposeAsync() {
// stop timer and wait for any lingering timer operations to finish
await _timer.DisposeAsync();
// wait for any in-flight operation to complete
if(!(_previousOperationTask?.IsCompleted ?? true)) {
await _previousOperationTask;
}
// flush all remaining messages
while(_logs.Any()) {
await FlushAsync();
}
}
결론
그게 다야 LambdaSharp가 Blazor WebAssembly 프런트엔드 앱에 대해 CloudWatch Logs 지원을 구현한 방법에 대한 이 비하인드 스토리 시리즈를 즐겼기를 바랍니다. 자신의 노력에 유용할 수 있기를 바랍니다.
해피 해킹!
Reference
이 문제에 관하여(웹 앱용 CloudWatch 로깅(3부)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://dev.to/lambdasharp/cloudwatch-logging-for-web-apps-part-3-59om
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
@inject LambdaSharpAppClient AppClient
@inject ILogger<Index> Logger
CloudWatch Logs는 로그 항목을 로그 스트림으로 구성합니다. 로그 스트림은 항목의 시간순입니다. 많은 로그 스트림이 동시에 존재할 수 있습니다. Blazor WebAssembly 앱 및 기타 단일 페이지 앱의 경우 첫 번째 로그 메시지가 생성될 때 요청 시 로그 스트림을 만드는 것이 좋습니다.
이 게시물의 코드는 단순화를 위해 수정되었습니다. 실제 구현에서는 주의를 산만하게 만드는 몇 가지 예외적인 경우를 더 다룹니다. 완전한 구현을 찾을 수 있습니다here.
로그 메시지 보내기
앱 클라이언트는 내부 누적기에 메시지를 큐에 넣습니다. 이를 통해 구현이 제한을 피하기 위해 한 번에 여러 메시지를 보낼 수 있습니다.
private readonly List<PutLogEventsRequestEntry> _logs = new List<PutLogEventsRequestEntry>();
private void SendMessage(string message) {
// queue message for server-side logging
_logs.Add(new PutLogEventsRequestEntry {
Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
Message = message ?? throw new ArgumentNullException(nameof(message))
});
}
시간 누적기
누산기는 대기 중인 메시지에 대한 타이머에 의해 매초 확인됩니다. 타이머 콜백은 먼저 이전 비동기 작업이 완료되었는지 확인합니다. 그런 다음 누적된 메시지를 플러시하려고 시도합니다.
private Task _previousOperationTask;
private void OnTimer(object _) {
if(!(_previousOperationTask?.IsCompleted ?? true)) {
// previous operation is still going; wait until next timer invocation to proceed
return;
}
// initialize invocation to FlushAsync(), but don't wait for it to finish
_previousOperationTask = FlushAsync();
}
일괄 전송
누적된 메시지의 첫 번째 배치를 보내기 전에 클라이언트는 로그 스트림이 생성되었는지 확인해야 합니다. 그런 다음 누적된 메시지를 1MB 크기 또는 10,000개 메시지 중 더 작은 것으로 제한된 배치로 청크합니다. 클라이언트가 오프라인일 가능성이 있기 때문에 작업이 실패하면 메시지가 누적기에 다시 삽입됩니다.
private string _logStreamName;
private string _sequenceToken;
private async Task FlushAsync() {
// check if any messages are pending
if(!_logs.Any()) {
return;
}
// check if a log stream must be created
if(_logStreamName == null) {
_logStreamName = AppInstanceId;
var response = await CreateLogStreamAsync(new CreateLogStreamRequest {
LogStreamName = _logStreamName
});
if(response.Error != null) {
Console.WriteLine($"*** ERROR: unable to create log stream: {_logStreamName} (Error: {response.Error})");
return;
}
}
// NOTE (2020-08-06, bjorg): we limit the number of log message we send in the unlikely event that we have too many
// See: https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_PutLogEvents.html
const int MaxPayloadSize = 1_048_576;
const int MaxMessageCount = 10_000;
// consume as many accumulated log messages as possible
var payloadByteCount = 0;
var logs = _logs.Take(MaxMessageCount).TakeWhile(log => {
var logMessageByteCount = Encoding.UTF8.GetByteCount(log.Message) + 26;
if((payloadByteCount + logMessageByteCount) >= MaxPayloadSize) {
return false;
}
payloadByteCount += logMessageByteCount;
return true;
}).ToList();
_logs.RemoveRange(0, logs.Count);
// send log messages to CloudWatch Logs
try {
var response = await PutLogEventsAsync(new PutLogEventsRequest {
LogStreamName = _logStreamName,
LogEvents = logs,
SequenceToken = _sequenceToken
});
// on error, re-insert the log messages and try again later
if(response.Error != null) {
_logs.InsertRange(0, logs);
return;
}
_sequenceToken = response.NextSequenceToken;
} catch {
// on exception, re-insert the log messages and try again later
_logs.InsertRange(0, logs);
}
}
처분
마지막으로, 클라이언트는 누적기에서 보류 중인 모든 메시지가 CloudWatch로 전송되도록 하기 위해 폐기될 때 최종 플러시 작업을 수행합니다.
async ValueTask IAsyncDisposable.DisposeAsync() {
// stop timer and wait for any lingering timer operations to finish
await _timer.DisposeAsync();
// wait for any in-flight operation to complete
if(!(_previousOperationTask?.IsCompleted ?? true)) {
await _previousOperationTask;
}
// flush all remaining messages
while(_logs.Any()) {
await FlushAsync();
}
}
결론
그게 다야 LambdaSharp가 Blazor WebAssembly 프런트엔드 앱에 대해 CloudWatch Logs 지원을 구현한 방법에 대한 이 비하인드 스토리 시리즈를 즐겼기를 바랍니다. 자신의 노력에 유용할 수 있기를 바랍니다.
해피 해킹!
Reference
이 문제에 관하여(웹 앱용 CloudWatch 로깅(3부)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://dev.to/lambdasharp/cloudwatch-logging-for-web-apps-part-3-59om
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
async ValueTask IAsyncDisposable.DisposeAsync() {
// stop timer and wait for any lingering timer operations to finish
await _timer.DisposeAsync();
// wait for any in-flight operation to complete
if(!(_previousOperationTask?.IsCompleted ?? true)) {
await _previousOperationTask;
}
// flush all remaining messages
while(_logs.Any()) {
await FlushAsync();
}
}
그게 다야 LambdaSharp가 Blazor WebAssembly 프런트엔드 앱에 대해 CloudWatch Logs 지원을 구현한 방법에 대한 이 비하인드 스토리 시리즈를 즐겼기를 바랍니다. 자신의 노력에 유용할 수 있기를 바랍니다.
해피 해킹!
Reference
이 문제에 관하여(웹 앱용 CloudWatch 로깅(3부)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/lambdasharp/cloudwatch-logging-for-web-apps-part-3-59om텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)