pondelok, 26 august 2013 08:25 Written by 2629 times
Rate this item
(1 Vote)

C# - Vyberanie časti textov z error logov

Rozhodol som sa pridať ďalší príspevok hlavne pre začínajúcich programátorov v c#, ktorí potrebujú napísať hlavne rýchlo, jednoduchú aplikáciu.
Snažil som sa napísať triedu čo najzrozumitelnejšie a najjednoduchšie, pritom, aby boli použité niektoré základné rysy objektového programovania: vytvorenie a použitie triedy a objektu, zapúzdrenie, vytvorenie vlastnosti (tzv. "properties"), atď.

 Aplikácia Read_error_log.exe vie otvoriť a prečitať naraz niekolko error logov a vytiahnuť z nich tie časti, ktoré obsahujú slovo error. Poskladať výstup do jednoduchého reťazca, ktorý je možné potom použiť napríklad ako informáciu pre administrátorov alebo aj pre užívateľov, v prípade potreby podrobnejších informácií o chybách a zlyhaniach DB serverov alebo iných aplikácii. V tomto prípade ide o aplikáciu na kontrolu databázovych alert logov z ORACLE databáz. V prípade, že sa nájde v logoch reťazec error, je do výstupu priložená celá čast správy logu, no a túto časť je možne potom poslať mailom napr. administrátorovi.
Ide o jednoduchú konzolovú aplikáciu, ktorej časť zdrojového súboru po úprave je možne použiť aj v iných programoch.  

Jednoduchy popis:

program obsahuje - triedu ORACLE_AlertLogReader :

public class ORACLE_AlertLogReader: IDisposable 
    {
        private const String CRLF = "\r\n";
        private int LAST_BYTES; 
        public int Last_bytes 
        {
            get
            {
                return this.LAST_BYTES;
            }
            set
            {
                this.LAST_BYTES = value;
            }
        }
        public ORACLE_AlertLogReader()
        {
            this.Last_bytes = 0;
        }
        
        private bool Is_header(String line)
        {
            String mask = line.Length >= 4 ? line.Substring(0, 4) : "";
            switch (mask)
            {
                case "Mon ":
                case "Tue ":
                case "Wen ":
                case "Thu ":
                case "Fri ":
                case "Sat ":
                case "Sun ":
                    return true;
                default:
                    return false;
            }
        }
        private bool Is_error(String line)
        {
            return line.IndexOf("error", StringComparison.OrdinalIgnoreCase) > -1;
        }
        public String Read_error_log(String filename)
        {
            String result = null;
            using (StreamReader reader = new StreamReader(filename))
            {
                // read and check last log lines ...
                if (reader.BaseStream.Length > this.Last_bytes)
                {
                    reader.BaseStream.Seek(-this.Last_bytes, SeekOrigin.End);
                }
                                
                String line = null;
                String block = null;
                while ((line = reader.ReadLine()) != null)
                {
                    if (this.Is_header(line))
                    {   
                        // look up for an error in the block ...
                        if (block.Length > 0)
                        { 
                            if (this.Is_error(block))
                                result += (block + ORACLE_AlertLogReader.CRLF);
                            block = null;
                        }
                    }
                    block += (line + ORACLE_AlertLogReader.CRLF);
                }
                if (block.Length > 0 && this.Is_error(block))
                    result += (block + ORACLE_AlertLogReader.CRLF);
            }
            return result;
        }
        public String Read_error_logs(string[,] LOG_FILES)
        {
            String output = null;
            String dbname = null;
            String logfile = null;
            String dboutput = String.Empty;
            for (int i = 0; i < LOG_FILES.GetLength(0); i++)
            {
                dbname = LOG_FILES[i, 0];
                logfile = LOG_FILES[i, 1];
                dboutput = this.Read_error_log(logfile);
                if (dboutput != null)
                {
                    output += (dbname + ORACLE_AlertLogReader.CRLF + dboutput);
                }
            }
            return output;
        }
    
        public void  Dispose()
        {
 	        //throw new NotImplementedException();
        }
    }

 

V triede sa nachádzajú privátne (tzv. "súkromne" metódy, ktoré užívatel nevidí) metody. Is_header(String line) - metoda zistí, či riadok error logu je hlavička správy. Metóda Is_error(String line) zistí, či sa v riadku nachádza slovo "error". Túto metódu je jednoduche doplniť aj o ďalšie možnosti hladania podvýrazov alebo doplniť o iné obmedzenia. Ďalej sa v triede nachádzaju verejné metódy Read_error_log(String filename) - táto metóda prečita "alert log" a výstup z nej je reťazec s obsahom len tých časti správ, ktoré obsahujú reťazec "error", metóda String Read_error_logs(string[,] LOG_FILES)  prečita niekoľko alert logov naraz. V podstate ako vidieť, metóda volá v cykle metódu Read_error_log, kedže vstupny parameter je pole reťazcov - v tomto prípade ciest súborov - "alert logov".  
 
V triede program je vytvoreny objekt:

ORACLE_AlertLogReader alr = new ORACLE_AlertLogReader();

 , ktorému som nastavil, aby prečítal len 2kB "alr.Last_bytes = 1024 * 2;" zo spodu súboru a zároveň vyvolal metódu na prečítanie výstupu

output = alr.Read_error_logs(LOG_FILES);

 ktorý je uložený v reťazcovej premennej output. Ja som obsah tejto premennej nechal vypísať metódou drzo na obrazovku : 

Console.WriteLine(output);

 Kód v c# :

 class Program
    {
        static void Main(string[] args)
        {
            string[,] LOG_FILES = new String[,] {
                                                    {"DB1","C:\\bendak\\applications\\C#_app\\Read_error_log\\alert_db1.log"}, 
                                                    {"DB2","C:\\bendak\\applications\\C#_app\\Read_error_log\\alert_db2.log"}
                                                };
            String output = null;
            using (ORACLE_AlertLogReader alr = new ORACLE_AlertLogReader())
            {
                alr.Last_bytes = 1024 * 2; // read from 2kB from the end of the log file ...
                output = alr.Read_error_logs(LOG_FILES);                
            }
            if (output == null)
                output = "No errors in any DB logs in last 2kB ...";
            Console.WriteLine(output);
        }
    }


Verím, že program pomôže začínajúcim programátorom aspoň na začiatok. Predpokladám, že programátor už ovláda aspoň základné pojmy a rysy objektového programovania a už má za sebou naprogramovaných zopár malých aplikácii.  Kompletný zdrojový kód je v prílohe.
 
S pozdravom AB:)

Last modified on utorok, 10 september 2013 08:15
Alojz Benďák

Autor je administrátor webu a venuje sa programovaniu takmer 23 rokov.

  • prvý kontakt s počítačmi na strednej škole - PMD 1,2,3, PP06, Atari, Sinclair (programovanie v jazyku: strojový kód, basic a pascal):  2 roky
  • na VŠ - jazyky Turbo pascal a Turbo C: 1rok
  • programovanie databázového ekonomického širokoškálneho software v Delphi a C++ Borland (databázy Paradox): 8 rokov v Codex s.r.o. Nitra
  • programovanie webových aplikácií na databázach MySQL a MS SQL server: 1 roky na živnosť (firmy: Hermes Nitra, Schindler v Bratislave)
  • pracoval ako DBA v Homecredit Brno a vo VÚB Bratislava ako vyvojár + optimalizácie v SQL a PL/SQL na Oracle 11g (firma Accenture)
  • programoval programy pre súkromné firmy pre HW na skenovanie povrchov nádrží pre skladovanie tekutých palív (pristroj Leica)
  • momentálne pracuje ako DBA pod Oracle 11g a ako vyvojár vnútropodnikových databázových aplikacií v PHP, SQL, C++, C# a interface v PL/SQL: 11 rokov
  • ďalej programuje s HTML, CSS, Ajax, Javascript, VBA, MS visual C++ a C# malé podporné aplikácie
  • certifikát SQL expert for Oracle 11g   
  • spolupracuje a aktívne učí pre počítačové firmy: Lapis, IVIT v Nitre