チェンジセット 1479 (default)


以下の違いを無視:
日時:
2020/03/21 23:05:01 (5年前)
更新者:
hizuya@…
ログメッセージ:
  • ReadText.ReadText が行の途中までを返すことがあるのにも関わらず、LogEntryParser がそれに対応していなかったので修正。
場所:
TinyLogViewer/trunk
ファイル:
1個の追加
5個の更新

凡例:

未変更
追加
削除
  • TinyLogViewer/trunk/TinyLogViewer/Sources/LogBlock.cs

    r1476 r1479  
    5959 
    6060        /// <summary> 
     61        /// 最後の行の行末の種類。 
     62        /// </summary> 
     63        [DebuggerBrowsable(DebuggerBrowsableState.Never)] 
     64        private LineEndType lastLineEndType; 
     65 
     66        /// <summary> 
    6167        /// 次のログブロック。 
    6268        /// </summary> 
     
    7278        /// <param name="bytesCount">データの長さを表すバイト数。</param> 
    7379        /// <param name="lineNumber">行番号。</param> 
    74         internal LogBlock(LogFile logFile, long startPosition, int bytesCount, long lineNumber) 
     80        /// <param name="lineEndType">行末の種類。</param> 
     81        internal LogBlock( 
     82            LogFile logFile, 
     83            long startPosition, 
     84            int bytesCount, 
     85            long lineNumber, 
     86            LineEndType lineEndType) 
    7587        { 
    7688            this.logFile = logFile; 
     
    8092 
    8193            lineCount = 1; 
     94            lastLineEndType = lineEndType; 
    8295        } 
    8396 
     
    122135            { 
    123136                return lineCount; 
     137            } 
     138        } 
     139 
     140        /// <summary> 
     141        /// 最後の行の行末の種類を取得します。 
     142        /// </summary> 
     143        /// <value> 
     144        /// 最後の行の行末の種類。 
     145        /// </value> 
     146        internal LineEndType LastLineEndType 
     147        { 
     148            get 
     149            { 
     150                return lastLineEndType; 
    124151            } 
    125152        } 
     
    148175        /// <param name="newLineBytesCount">データの長さを表すバイト数。</param> 
    149176        /// <param name="newLineLineNumber">行番号。</param> 
     177        /// <param name="newLineEndType">行末の種類。</param> 
    150178        /// <returns> 
    151179        /// 追加先のログブロック。 
    152180        /// このインスタンスが対象としているログファイルと異なる場合は新しいログブロックが作成されて返されます。 
    153181        /// </returns> 
    154         internal LogBlock AppendLine(LogFile newLineLogFile, long newLineStartPosition, int newLineBytesCount, long newLineLineNumber) 
     182        internal LogBlock AppendLine( 
     183            LogFile newLineLogFile, 
     184            long newLineStartPosition, 
     185            int newLineBytesCount, 
     186            long newLineLineNumber, 
     187            LineEndType newLineEndType) 
    155188        { 
    156189            Debug.Assert(nextBlock == null, "nextBlock is not null."); 
     
    160193            { 
    161194                Debug.Assert(startPosition + bytesCount == newLineStartPosition, "Invalid start position."); 
    162                 //Debug.Assert(lineNumber + lineCount == newLineLineNumber, "Invalid line number."); 
    163                 // FIXME 
     195                Debug.Assert(lineNumber + lineCount + (lastLineEndType == LineEndType.None ? -1 : 0) == newLineLineNumber, "Invalid line number."); 
    164196 
    165197                bytesCount += newLineBytesCount; 
    166                 lineCount++; 
     198 
     199                // 直前が改行で終わっている場合 
     200                if (lastLineEndType != LineEndType.None) 
     201                { 
     202                    lineCount++; 
     203                } 
     204 
     205                lastLineEndType = newLineEndType; 
    167206                return this; 
    168207            } 
    169208 
    170209            // 新しいログファイルの場合 
    171             return nextBlock = new LogBlock(newLineLogFile, newLineStartPosition, newLineBytesCount, newLineLineNumber); 
     210            return nextBlock = new LogBlock(newLineLogFile, newLineStartPosition, newLineBytesCount, newLineLineNumber, newLineEndType); 
    172211        } 
    173212    } 
  • TinyLogViewer/trunk/TinyLogViewer/Sources/Parser/LogEntryParser.cs

    r1474 r1479  
    2323{ 
    2424    using System; 
     25    using System.Diagnostics; 
    2526    using System.Text; 
    2627    using System.Text.RegularExpressions; 
     
    4849 
    4950        /// <summary> 
     51        /// 改行が見付かるまで現在の行の値を持つ文字列ビルダー。 
     52        /// </summary> 
     53        private readonly StringBuilder currentLineBuilder; 
     54 
     55        /// <summary> 
    5056        /// 行を処理するデリゲート。 
    5157        /// </summary> 
    5258        private readonly ProcessAction process; 
    5359 
     60 
     61        /// <summary> 
     62        /// 改行が見付かるまで現在の行を保持するインスタンス。 
     63        /// </summary> 
     64        private LogLine currentLine; 
    5465 
    5566        /// <summary> 
     
    93104        /// <param name="startEntryRegex">ログエントリーの開始を検出する正規表現。</param> 
    94105        /// <param name="endEntryRegex">ログエントリーの終了を検出する正規表現。</param> 
     106        /// <param name="maxLineLength">処理対象とする 1 行の最大文字数。</param> 
    95107        /// <param name="logEntryDetected">検出したログエントリーを通知するデリゲート。</param> 
    96108        /// <exception cref="ArgumentNullException"> 
    97109        /// <paramref name="logEntryDetected"/> は <see langword="null"/> です。 
    98110        /// </exception> 
    99         public LogEntryParser(Regex startEntryRegex, Regex endEntryRegex, LogEntryDelegate logEntryDetected) 
     111        /// <exception cref="ArgumentOutOfRangeException"> 
     112        /// <paramref name="maxLineLength"/> が 16 未満です。 
     113        /// </exception> 
     114        public LogEntryParser( 
     115            Regex startEntryRegex, 
     116            Regex endEntryRegex, 
     117            int maxLineLength, 
     118            LogEntryDelegate logEntryDetected) 
    100119        { 
    101120            // 引数をチェック 
     
    103122            { 
    104123                throw new ArgumentNullException("logEntryDetected"); 
     124            } 
     125 
     126            if (maxLineLength < 16) 
     127            { 
     128                // FIXME メッセージ 
     129                throw new ArgumentOutOfRangeException(); 
    105130            } 
    106131 
     
    108133            this.endEntryRegex = endEntryRegex; 
    109134            this.logEntryDetected = logEntryDetected; 
     135 
     136            currentLineBuilder = new StringBuilder(maxLineLength); 
    110137 
    111138            // 処理するためのデリゲートを準備 
     
    142169        /// <param name="bytesCount">データの長さを表すバイト数。</param> 
    143170        /// <param name="lineNumber">行番号。</param> 
    144         /// <param name="value">行を表す文字列。</param> 
    145         private delegate void ProcessAction(LogFile logFile, long startPosition, int bytesCount, long lineNumber, string value); 
     171        /// <param name="lineEndType">行末の種類。</param> 
     172        /// <param name="value"> 
     173        /// 行を表す文字列。 
     174        /// 前の行の続きの場合は <see langword="null"/>。 
     175        /// </param> 
     176        private delegate void ProcessAction( 
     177            LogFile logFile, 
     178            long startPosition, 
     179            int bytesCount, 
     180            long lineNumber, 
     181            LineEndType lineEndType, 
     182            string value); 
    146183 
    147184 
     
    157194        /// <param name="charsIndex"><paramref name="chars"/> の開始位置。</param> 
    158195        /// <param name="charsLength"><paramref name="chars"/> の <paramref name="charsIndex"/> からの有効な長さ。</param> 
    159         public void FeedLine(LogFile logFile, long startPosition, int bytesCount, long lineNumber, LineEndType lineEndType, char[] chars, int charsIndex, int charsLength) 
    160         { 
     196        public void FeedLine( 
     197            LogFile logFile, 
     198            long startPosition, 
     199            int bytesCount, 
     200            long lineNumber, 
     201            LineEndType lineEndType, 
     202            char[] chars, 
     203            int charsIndex, 
     204            int charsLength) 
     205        { 
     206            if (currentLine != null) 
     207            { 
     208                // ログファイルが同じで、または1行の長さが 2G 未満の場合は、末尾に追加 
     209                if (currentLine.LogFile == logFile 
     210                    && currentLine.BytesCount + (long)bytesCount <= int.MaxValue) 
     211                { 
     212                    Debug.Assert(currentLine.LineNumber == lineNumber, "Invalid line number."); 
     213                    Debug.Assert(currentLine.StartPosition + currentLine.BytesCount == startPosition, "Invalid start position."); 
     214 
     215                    // 末尾に追加 
     216                    currentLine.Append(bytesCount, chars, charsIndex, charsLength); 
     217 
     218                    // まだ行の途中の場合 
     219                    if (lineEndType == LineEndType.None) 
     220                    { 
     221                        return; 
     222                    } 
     223 
     224                    // 改行で終わっている場合 
     225                    ProcessCurrentLine(lineEndType); 
     226                    return; 
     227                } 
     228 
     229                // 手前までを確定 
     230                ProcessCurrentLine(LineEndType.None); 
     231            } 
     232 
     233            // 新しい行が改行で終わっていない場合 
     234            if (lineEndType == LineEndType.None) 
     235            { 
     236                currentLine = new LogLine(logFile, startPosition, bytesCount, lineNumber, currentLineBuilder, chars, charsIndex, charsLength); 
     237                return; 
     238            } 
     239 
     240            // 行の続きの場合は null 
     241            string value 
     242                = firstLogBlock == null || lastLogBlock.LastLineEndType != LineEndType.None 
     243                    ? new string(chars, charsIndex, charsLength) 
     244                    : null; 
     245 
    161246            process( 
    162247                logFile, 
     
    164249                bytesCount, 
    165250                lineNumber, 
    166                 new string(chars, charsIndex, charsLength)); 
     251                lineEndType, 
     252                value); 
    167253        } 
    168254 
     
    183269        /// <param name="bytesCount">データの長さを表すバイト数。</param> 
    184270        /// <param name="lineNumber">行番号。</param> 
    185         /// <param name="value">行を表す文字列。</param> 
    186         private void ProcessSingleLine(LogFile logFile, long startPosition, int bytesCount, long lineNumber, string value) 
    187         { 
    188             logEntryDetected(new LogEntry(new LogBlock(logFile, startPosition, bytesCount, lineNumber), value)); 
    189             GC.KeepAlive(value); 
     271        /// <param name="lineEndType">行末の種類。</param> 
     272        /// <param name="value"> 
     273        /// 行を表す文字列。 
     274        /// 前の行の続きの場合は <see langword="null"/>。 
     275        /// </param> 
     276        private void ProcessSingleLine( 
     277            LogFile logFile, 
     278            long startPosition, 
     279            int bytesCount, 
     280            long lineNumber, 
     281            LineEndType lineEndType, 
     282            string value) 
     283        { 
     284            // 1 行で完結する場合 
     285            if (lineEndType != LineEndType.None && firstLogBlock == null) 
     286            { 
     287                Debug.Assert(value != null, "value is null."); 
     288                logEntryDetected(new LogEntry(new LogBlock(logFile, startPosition, bytesCount, lineNumber, lineEndType), value)); 
     289                GC.KeepAlive(value); 
     290                return; 
     291            } 
     292 
     293            // 行を追加 
     294            AppendLine(logFile, startPosition, bytesCount, lineNumber, lineEndType, value); 
    190295        } 
    191296 
     
    197302        /// <param name="bytesCount">データの長さを表すバイト数。</param> 
    198303        /// <param name="lineNumber">行番号。</param> 
    199         /// <param name="value">行を表す文字列。</param> 
    200         private void ProcessStartDetection(LogFile logFile, long startPosition, int bytesCount, long lineNumber, string value) 
     304        /// <param name="lineEndType">行末の種類。</param> 
     305        /// <param name="value"> 
     306        /// 行を表す文字列。 
     307        /// 前の行の続きの場合は <see langword="null"/>。 
     308        /// </param> 
     309        private void ProcessStartDetection( 
     310            LogFile logFile, 
     311            long startPosition, 
     312            int bytesCount, 
     313            long lineNumber, 
     314            LineEndType lineEndType, 
     315            string value) 
    201316        { 
    202317            // ログエントリーの開始がある場合 
    203             if (startEntryRegex != null && startEntryRegex.IsMatch(value)) 
     318            if (value != null && startEntryRegex.IsMatch(value)) 
    204319            { 
    205320                CommitLogEntry(); 
     
    207322 
    208323            // 行を追加 
    209             AppendLine(logFile, startPosition, bytesCount, lineNumber, value); 
     324            AppendLine(logFile, startPosition, bytesCount, lineNumber, lineEndType, value); 
    210325        } 
    211326 
     
    217332        /// <param name="bytesCount">データの長さを表すバイト数。</param> 
    218333        /// <param name="lineNumber">行番号。</param> 
    219         /// <param name="value">行を表す文字列。</param> 
    220         private void ProcessEndDetection(LogFile logFile, long startPosition, int bytesCount, long lineNumber, string value) 
     334        /// <param name="lineEndType">行末の種類。</param> 
     335        /// <param name="value"> 
     336        /// 行を表す文字列。 
     337        /// 前の行の続きの場合は <see langword="null"/>。 
     338        /// </param> 
     339        private void ProcessEndDetection( 
     340            LogFile logFile, 
     341            long startPosition, 
     342            int bytesCount, 
     343            long lineNumber, 
     344            LineEndType lineEndType, 
     345            string value) 
    221346        { 
    222347            // 行を追加 
    223             AppendLine(logFile, startPosition, bytesCount, lineNumber, value); 
     348            AppendLine(logFile, startPosition, bytesCount, lineNumber, lineEndType, value); 
    224349 
    225350            // ログエントリーの終了がある場合 
    226             if (endEntryRegex.IsMatch(value)) 
     351            if (value != null && endEntryRegex.IsMatch(value)) 
    227352            { 
    228353                CommitLogEntry(); 
     
    237362        /// <param name="bytesCount">データの長さを表すバイト数。</param> 
    238363        /// <param name="lineNumber">行番号。</param> 
    239         /// <param name="value">行を表す文字列。</param> 
    240         private void ProcessStartAndEndDetection(LogFile logFile, long startPosition, int bytesCount, long lineNumber, string value) 
     364        /// <param name="lineEndType">行末の種類。</param> 
     365        /// <param name="value"> 
     366        /// 行を表す文字列。 
     367        /// 前の行の続きの場合は <see langword="null"/>。 
     368        /// </param> 
     369        private void ProcessStartAndEndDetection( 
     370            LogFile logFile, 
     371            long startPosition, 
     372            int bytesCount, 
     373            long lineNumber, 
     374            LineEndType lineEndType, 
     375            string value) 
    241376        { 
    242377            // ログエントリーの開始がある場合 
    243             if (startEntryRegex != null && startEntryRegex.IsMatch(value)) 
     378            if (value != null && startEntryRegex.IsMatch(value)) 
    244379            { 
    245380                CommitLogEntry(); 
     
    247382 
    248383            // 行を追加 
    249             AppendLine(logFile, startPosition, bytesCount, lineNumber, value); 
     384            AppendLine(logFile, startPosition, bytesCount, lineNumber, lineEndType, value); 
    250385 
    251386            // ログエントリーの終了がある場合 
    252             if (endEntryRegex.IsMatch(value)) 
     387            if (value != null && endEntryRegex.IsMatch(value)) 
    253388            { 
    254389                CommitLogEntry(); 
     
    257392 
    258393        /// <summary> 
     394        /// 現在の行を元に処理を行います。 
     395        /// </summary> 
     396        /// <param name="lineEndType">行末の種類。</param> 
     397        private void ProcessCurrentLine(LineEndType lineEndType) 
     398        { 
     399            currentLine.SetLineEnd(lineEndType); 
     400 
     401            process( 
     402                currentLine.LogFile, 
     403                currentLine.StartPosition, 
     404                currentLine.BytesCount, 
     405                currentLine.LineNumber, 
     406                lineEndType, 
     407                currentLine.ToString()); 
     408 
     409            currentLine = null; 
     410            currentLineBuilder.Length = 0; 
     411        } 
     412 
     413        /// <summary> 
    259414        /// 行を追加します。 
    260415        /// </summary> 
     
    263418        /// <param name="bytesCount">データの長さを表すバイト数。</param> 
    264419        /// <param name="lineNumber">行番号。</param> 
    265         /// <param name="value">行を表す文字列。</param> 
    266         private void AppendLine(LogFile logFile, long startPosition, int bytesCount, long lineNumber, string value) 
     420        /// <param name="lineEndType">行末の種類。</param> 
     421        /// <param name="value"> 
     422        /// 行を表す文字列。 
     423        /// 前の行の続きの場合は <see langword="null"/>。 
     424        /// </param> 
     425        private void AppendLine( 
     426            LogFile logFile, 
     427            long startPosition, 
     428            int bytesCount, 
     429            long lineNumber, 
     430            LineEndType lineEndType, 
     431            string value) 
    267432        { 
    268433            // ログエントリーが空の場合 
    269434            if (firstLogBlock == null) 
    270435            { 
    271                 lastLogBlock = firstLogBlock = new LogBlock(logFile, startPosition, bytesCount, lineNumber); 
     436                Debug.Assert(value != null, "value is null."); 
     437                lastLogBlock = firstLogBlock = new LogBlock(logFile, startPosition, bytesCount, lineNumber, lineEndType); 
    272438                logEntryFirstValue = value; 
    273439                return; 
     
    277443            if (logEntryValueBuilder == null) 
    278444            { 
    279                 logEntryValueBuilder = new StringBuilder((logEntryFirstValue.Length + value.Length) * 2); 
     445                logEntryValueBuilder = new StringBuilder((logEntryFirstValue.Length + (value != null ? value.Length : 0)) * 2); 
    280446                logEntryValueBuilder.Append(logEntryFirstValue); 
    281447                logEntryFirstValue = null; 
    282448            } 
    283449 
    284             logEntryValueBuilder.Append(value); 
     450            if (value != null) 
     451            { 
     452                // 行の続きではない場合 
     453                logEntryValueBuilder.Append(value); 
     454            } 
     455            else 
     456            { 
     457                // 行の続きで行末記号がある場合 
     458                switch (lineEndType) 
     459                { 
     460                    case LineEndType.CarriageReturn: 
     461                        logEntryValueBuilder.Append('\r'); 
     462                        break; 
     463                    case LineEndType.LineFeed: 
     464                        logEntryValueBuilder.Append('\n'); 
     465                        break; 
     466                    case LineEndType.CarriageReturnAndLineFeed: 
     467                        logEntryValueBuilder.Append('\r'); 
     468                        logEntryValueBuilder.Append('\n'); 
     469                        break; 
     470                } 
     471            } 
    285472 
    286473            // 末尾に追加 
    287             lastLogBlock = lastLogBlock.AppendLine(logFile, startPosition, bytesCount, lineNumber); 
     474            lastLogBlock = lastLogBlock.AppendLine(logFile, startPosition, bytesCount, lineNumber, lineEndType); 
    288475        } 
    289476 
  • TinyLogViewer/trunk/TinyLogViewer/Sources/Parser/LogParser.cs

    r1475 r1479  
    4545                    new Regex("^[^ ]+ [^:]+: [0-9]+ : ", RegexOptions.ExplicitCapture), 
    4646                    null, 
     47                    1024 * 1024, 
    4748                    logEntryDetected); 
    4849        } 
  • TinyLogViewer/trunk/TinyLogViewer/TinyLogViewer.csproj

    r1476 r1479  
    8888    <Compile Include="Sources\Logging.cs" /> 
    8989    <Compile Include="Sources\LogBlock.cs" /> 
     90    <Compile Include="Sources\LogLine.cs" /> 
    9091    <Compile Include="Sources\LogProfile.cs" /> 
    9192    <Compile Include="Sources\LogProfileEventArgs.cs" /> 
  • TinyLogViewer/trunk/TinyLogViewerTest/Sources/Parser/LogEntryParserTest.cs

    r1474 r1479  
    6363                    new Regex("^[^ ]+ [^:]+: [0-9]+ : ", RegexOptions.ExplicitCapture), 
    6464                    null, 
     65                    1024, 
    6566                    delegate(LogEntry logEntry) 
    6667                    { 
     
    106107                        5, 
    107108                        @"FCosft.TinyLogViewer.Test Information: 103 : Message3. 
     109XX yyy. 
     110    ProcessId=1001 
     111    ThreadId=2 
     112    DateTime=2020-01-23T10:20:32.1234567+09:00 
     113"), 
     114                })); 
     115        } 
     116 
     117        /// <summary> 
     118        /// 長い行の有るログエントリーの開始を検出ルテストです。 
     119        /// </summary> 
     120        [Test] 
     121        public void TestStartDetectionWithLongLine() 
     122        { 
     123            List<LogEntryInfo> logEntryInfos = new List<LogEntryInfo>(); 
     124            LogEntryParser parser 
     125                = new LogEntryParser( 
     126                    new Regex("^[^ ]+ [^:]+: [0-9]+ : ", RegexOptions.ExplicitCapture), 
     127                    null, 
     128                    60, 
     129                    delegate(LogEntry logEntry) 
     130                    { 
     131                        logEntryInfos.Add(new LogEntryInfo(logEntry)); 
     132                    }); 
     133 
     134            Execute( 
     135                parser, 
     136                @"FCosft.TinyLogViewer.Test Information: 101 : Message1. 
     137    ProcessId=1001 
     138    ThreadId=2 
     139    DateTime=2020-01-23T10:20:30.1234567+09:00 
     140FCosft.TinyLogViewer.Test Information: 102 : Message2. 
     141FCosft.TinyLogViewer.Test Information: 103 : 678901234567890FCosft.TinyLogViewer.Test Information: 103 : Message3. 
     142XX yyy. 
     143    ProcessId=1001 
     144    ThreadId=2 
     145    DateTime=2020-01-23T10:20:32.1234567+09:00 
     146", 
     147                120); 
     148 
     149            Assert.That( 
     150                logEntryInfos, 
     151                Is.EqualTo(new[] 
     152                { 
     153                    new LogEntryInfo( 
     154                        DefulatLogFilePath, 
     155                        1, 
     156                        4, 
     157                        @"FCosft.TinyLogViewer.Test Information: 101 : Message1. 
     158    ProcessId=1001 
     159    ThreadId=2 
     160    DateTime=2020-01-23T10:20:30.1234567+09:00 
     161"), 
     162                    new LogEntryInfo( 
     163                        DefulatLogFilePath, 
     164                        5, 
     165                        1, 
     166                        @"FCosft.TinyLogViewer.Test Information: 102 : Message2. 
     167"), 
     168                    new LogEntryInfo( 
     169                        DefulatLogFilePath, 
     170                        6, 
     171                        5, 
     172                        @"FCosft.TinyLogViewer.Test Information: 103 : 6789012345678 
    108173XX yyy. 
    109174    ProcessId=1001 
     
    152217        } 
    153218 
     219        /// <summary> 
     220        /// 文字列をパーサに供給します。 
     221        /// </summary> 
     222        /// <param name="parser">パーサ。</param> 
     223        /// <param name="text">文字列。</param> 
     224        /// <param name="bufferSize">バッファサイズ。</param> 
     225        private static void Execute(LogEntryParser parser, string text, int bufferSize) 
     226        { 
     227            LogFile logFile = new LogFile(DefulatLogFilePath, 0x1000, Encoding.Unicode, Encoding.Unicode, 0x1000); 
     228 
     229            TextDecoder decoder 
     230                = new TextDecoder( 
     231                    bufferSize, 
     232                    Encoding.Unicode, 
     233                    delegate(long startPosition, int bytesCount, long lineNumber, LineEndType lineEndType, char[] chars, int charsIndex, int charsLength) 
     234                    { 
     235                        parser.FeedLine(logFile, startPosition, bytesCount, lineNumber, lineEndType, chars, charsIndex, charsLength); 
     236                    }); 
     237 
     238            using (MemoryStream memoryStream = new MemoryStream(Encoding.Unicode.GetBytes(text), false)) 
     239            { 
     240                decoder.DecodeFrom(memoryStream); 
     241            } 
     242 
     243            parser.Flush(); 
     244        } 
     245 
    154246 
    155247        /// <summary> 
詳しい使い方は TracChangeset を参照してください。