/// <summary>
        /// When overriden, return the header fields for the log file with a given <paramref name="context"/>,
        /// or 'null' if none is found
        /// </summary>
        protected virtual async Task <string[]> TryGetHeaderFields(DelimitedTextLogContext context, CancellationToken stopToken)
        {
            var  position   = 0;
            long lineNumber = 0;

            using (var stream = new FileStream(context.FilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                using (var reader = new LineReader(stream, _encoding, _bufferSize))
                {
                    while (position < context.Position)
                    {
                        stopToken.ThrowIfCancellationRequested();
                        var(line, consumed) = await reader.ReadAsync(stopToken);

                        if (line is null)
                        {
                            break;
                        }
                        lineNumber++;
                        position += consumed;
                        if (IsHeaders(line, lineNumber))
                        {
                            return(ParseHeadersLine(line));
                        }
                    }
                }

            return(null);
        }
Beispiel #2
0
        protected override async Task <string[]> TryGetHeaderFields(DelimitedTextLogContext context, CancellationToken stopToken)
        {
            if (_defaultHeaders is not null)
            {
                return(ParseHeadersLine(_defaultHeaders));
            }

            return(await base.TryGetHeaderFields(context, stopToken));
        }
Beispiel #3
0
        protected override W3SVCRecord CreateRecord(DelimitedTextLogContext context, Dictionary <string, string> data)
        {
            var timestamp = DateTime.UtcNow;

            if (data.TryGetValue("date", out var date) && data.TryGetValue("time", out var time))
            {
                // date and time field is UTC-based: https://docs.microsoft.com/en-us/windows/win32/http/w3c-logging
                timestamp = DateTime.Parse(date + "T" + time + "Z", null, DateTimeStyles.RoundtripKind);
            }

            return(new W3SVCRecord(timestamp, data));
        }
        protected override KeyValueLogRecord CreateRecord(DelimitedTextLogContext context, Dictionary <string, string> data)
        {
            var timestamp = DateTime.Now;

            if (data.TryGetValue("TimestampUtc", out var timestampText))
            {
                timestamp = DateTime.Parse(timestampText, null, DateTimeStyles.AssumeUniversal);
            }
            else if (data.TryGetValue("Timestamp", out timestampText))
            {
                timestamp = DateTime.Parse(timestampText, null, DateTimeStyles.AssumeLocal);
            }
            return(new KeyValueLogRecord(timestamp, data));
        }
        /// <inheritdoc/>
        public async Task ParseRecordsAsync(DelimitedTextLogContext context, IList <IEnvelope <TData> > output,
                                            int recordCount, CancellationToken stopToken = default)
        {
            if (context.Fields is null)
            {
                context.Fields = await TryGetHeaderFields(context, stopToken);
            }

            var count = 0;

            using (var stream = new FileStream(context.FilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
            {
                stream.Position = context.Position;
                using (var reader = new LineReader(stream, _encoding, _bufferSize))
                {
                    while (count < recordCount)
                    {
                        stopToken.ThrowIfCancellationRequested();
                        var(line, consumed) = await reader.ReadAsync(stopToken);

                        _logger.LogTrace("File: '{0}', line: '{1}', bytes: {2}", context.FilePath, line, consumed);
                        context.Position += consumed;

                        if (line is null)
                        {
                            break;
                        }

                        if (ShouldStopAndRollback(line, context))
                        {
                            context.Position -= consumed;
                            return;
                        }

                        context.LineNumber++;

                        if (IsHeaders(line, context.LineNumber))
                        {
                            context.Fields = ParseHeadersLine(line);
                            continue;
                        }
                        else if (IsComment(line))
                        {
                            continue;
                        }

                        try
                        {
                            // 'ParseDataFragments' and 'CreateRecord' might throw error, so we need to catch it and skip the record
                            var fragments = ParseDataFragments(line);

                            if (context.Fields is null)
                            {
                                _logger.LogWarning("Unknown field mapping, skipping line {0}", context.LineNumber);
                                continue;
                            }
                            var dict = new Dictionary <string, string>();
                            for (var i = 0; i < context.Fields.Length; i++)
                            {
                                if (i >= fragments.Length)
                                {
                                    break;
                                }
                                var(key, val) = KeyValueSelector(context.Fields[i], fragments[i]);
                                dict[key]     = val;
                            }

                            var record = CreateRecord(context, dict);

                            var envelope = new LogEnvelope <TData>(record, record.Timestamp, line, context.FilePath, context.Position, context.LineNumber);
                            output.Add(envelope);
                            count++;
                        }
                        catch (Exception ex)
                        {
                            _logger.LogError(ex, "Error processing record '{0}'", line);
                            continue;
                        }
                    }
                }
            }
        }
 /// <summary>
 /// When immplemented, create a record based on the parsed key-value pairs.
 /// </summary>
 protected abstract TData CreateRecord(DelimitedTextLogContext context, Dictionary <string, string> data);
 /// <summary>
 /// When implemented, returns true iff the line indicates that the reader should stop and rewind.
 /// </summary>
 protected virtual bool ShouldStopAndRollback(string line, DelimitedTextLogContext context) => false;