Java में regex के साथ लॉग फ़िल्टर करना




algorithm logging (2)

विवरण बहुत लंबा है, इसलिए कृपया मेरे साथ सहन करें:
मेरे पास 300 MB से लेकर 1.5 जीबी तक की लॉग फाइलें हैं, जो एक खोज कुंजी को फ़िल्टर करने की आवश्यकता होती है।

लॉग्स का प्रारूप कुछ ऐसा है:

24 May 2017 17:00:06,827 [INFO] 123456 (Blah : Blah1) Service-name:: Single line content
24 May 2017 17:00:06,828 [INFO] 567890 (Blah : Blah1) Service-name:: Content( May span multiple lines)
24 May 2017 17:00:06,829 [INFO] 123456 (Blah : Blah2) Service-name: Multiple line content. Printing Object[ ID1=fac-adasd ID2=123231
ID3=123108 Status=Unknown
Code=530007 Dest=CA
]
24 May 2017 17:00:06,830 [INFO] 123456 (Blah : Blah1) Service-name:: Single line content
4 May 2017 17:00:06,831 [INFO] 567890 (Blah : Blah2) Service-name:: Content( May span multiple lines)

खोज कुंजी 123456 को देखते हुए, मुझे निम्नलिखित को लाने की आवश्यकता है:

24 May 2017 17:00:06,827 [INFO] 123456 (Blah : Blah1) Service-name:: Single line content
24 May 2017 17:00:06,829 [INFO] 123456 (Blah : Blah2) Service-name: Multiple line content. Printing Object[ ID1=fac-adasd ID2=123231
ID3=123108 Status=Unknown
Code=530007 Dest=CA
]
24 May 2017 17:00:06,830 [INFO] 123456 (Blah : Blah1) Service-name:: Single line content

निम्नलिखित अक्साइट स्क्रिप्ट मेरे काम (बहुत धीरे धीरे) हो जाता है:

gawk '/([0-9]{1}|[0-9]{2})\s\w+\s[0-9]{4}/{n=0}/123456/{n=1} n'

1 जीबी आकार की लॉग फ़ाइल को खोजने के लिए लगभग 8 मिनट लगते हैं। और मुझे यह ऐसी कई फाइलों के लिए करना है इसे ऊपर छोड़ने के लिए, मेरे पास कई ऐसी खोज कुंजियां हैं, जो पूरे काम की असंभव बना देती हैं

मेरा प्रारंभिक समाधान मल्टीथ्रेडिंग का उपयोग करना है मैंने एक निश्चित थ्रेडपूल एक्सक्लोरर का उपयोग किया है, प्रत्येक फ़िल्टर्ड के लिए एक कार्य सबमिट किया है जिसे फ़िल्टर्ड करने की आवश्यकता है। कार्य विवरण के अंदर, मैंने जावा की रनटाइम () का उपयोग करते हुए नई प्रक्रिया बनाई है, जो बैप का उपयोग करके गायक स्क्रिप्ट को निष्पादित करेगा और एक फ़ाइल में आउटपुट लिखेंगे और फिर सभी फाइलों को मिला दिया जाएगा।

यद्यपि यह एक कमजोर तरीका हो सकता है, क्योंकि फ़िल्टरिंग I / O निर्भर करता है CPU के बजाय, यह मुझे प्रत्येक फ़ाइल पर लिपि को निष्पादित करने की तुलना में गति प्रदान करता है।

लेकिन यह अभी भी पर्याप्त नहीं है क्योंकि पूरी जानकारी को 2 घंटे लगते हैं, एक खोज कुंजी के लिए, लॉग फाइल के 27gb के साथ। औसतन, मेरे पास 4 ऐसी खोज कुंजियां हैं और उन्हें अपने सभी परिणामों को प्राप्त करना होगा और उन्हें एक साथ रखना होगा।

मेरी विधि कुशल नहीं है क्योंकि:

ए) यह प्रत्येक लॉग फ़ाइल को कई बार एक्सेस करता है जब एकाधिक खोज कुंजी दी जाती है और अधिक आई / ओ ओवरहेड भी हो जाता है।
बी) यह प्रत्येक थ्रेड के अंदर एक प्रक्रिया बनाने के ऊपर की ओर झुकती है।

इस सब का एक सरल समाधान, awk से दूर जा रहा है और कुछ रीजेक्स लाइब्रेरी का उपयोग करके जावा में पूरी चीज कर रहा है। यहाँ सवाल यह है कि क्या regex पुस्तकालय है जो मुझे इच्छित उत्पादन के साथ प्रदान कर सकता है?
Awk के साथ मेरे पास /filter/{action} संपत्ति है जो मुझे कई लाइनों की एक सीमा निर्दिष्ट करती है, जिसे कैप्चर किया जा सकता है (जैसा ऊपर देखा गया है)। मैं जावा के भीतर ही कैसे कर सकता हूं?

मैं सभी प्रकार के सुझावों के लिए खुला हूं। उदाहरण के लिए, एक अति विकल्प लॉग फ़ाइलों को एक साझा फाइल सिस्टम में स्टोर करना होगा जैसे S3 और एकाधिक कंप्यूटरों का उपयोग करके आउटपुट की प्रक्रिया करना।

मैं stackoverflow के लिए नया हूँ और मुझे यह भी पता नहीं है अगर मैं इसे यहां पोस्ट कर सकता हूं। लेकिन मैं पिछले हफ्ते इस पर काम कर रहा हूं और मुझे इसके बारे में मार्गदर्शन करने के लिए विशेषज्ञता के साथ किसी की ज़रूरत है। अग्रिम में धन्यवाद।


अगर आप अपना निष्पादन समय बढ़ाने की कोशिश कर रहे हैं तो जावा पर स्विच करना सबसे अच्छा विकल्प नहीं होगा, लेकिन अगर आप इसे पर विचार कर रहे हैं, तो मैंने एक जावा क्लास लिखा था जो मदद कर सकता है।

आप इसे एक या एक से अधिक कुंजी को एक साथ फाइल में खोजने के लिए उपयोग कर सकते हैं चूंकि आप एक लॉग फ़ाइल पढ़ रहे हैं, यह मानना ​​सुरक्षित है कि सभी लाइनें त्रुटियों के बिना उचित स्वरूप का पालन करती हैं। इसलिए पूरी लाइन को फेगेट-फॉरमेट-जांच करने के बजाय, यह केवल उस स्थान को छोड़ता है जहां कुंजी होना चाहिए (प्रथम के बाद अंक)), और इसकी तुलना आवश्यक मान से करता है (यह हमेशा एक नंबर मानता है)।

इसे इस तरह प्रयोग करें:

Set<Integer> keys = new HashSet();
keys.add(123456);
keys.add(314159);
/* synchronously (omitting 3rd argument prints to stdout) */
new KeySearch('path/to/file.log', keys).run();

/* asynchronously!!! (to use PrintStream, create the output file first) */
PrintStream ps1 = new PrintStream('lines-found1.log');
PrintStream ps2 = new PrintStream('lines-found2.log');
new Thread(new KeySearch('path/to/1.log', keys, ps1::println)).start();
new Thread(new KeySearch('path/to/2.log', keys, ps2::println)).start();

तीसरा तर्क एक कस्टम इंटरफ़ेस है KeySearch.Callback जो लाइनों को प्राप्त करता है जैसा कि वे पाए जाते हैं। मैं एक उदाहरण के रूप में एक विधि संदर्भ का उपयोग करता हूं, लेकिन यह आप कुछ भी हो सकता है। यहां क्लास है (कम से कम 8 जावा की आवश्यकता है)

import java.io.*;
import java.util.*;

public class KeySearch implements Runnable {
    public interface Callback { 
        void lineFound(String line); 
    }

    private final Set<Integer> keys;
    private final Callback callback;
    private final String name;

    public KeySearch(String fileName, Collection<Integer> keys) {
        this(fileName, keys, System.out::println);
    }

    public KeySearch(String fileName, Collection<Integer> keys, Callback call) {
        this.keys = new HashSet<>(keys);
        this.name = fileName;
        this.callback = call;
    }

    @Override
    public void run() {
        String s;
        try(FileReader fr = new FileReader(name); 
                BufferedReader br = new BufferedReader(fr)) {
            while ((s = readLine(br)) != null)
                if (matches(s)) callback.lineFound(s);
        } catch (IOException e) {
            System.err.println("Error reading " + name);
            throw new RuntimeException(e);
        }
    }

    private boolean matches(String line) {
        return keys.contains(getKeyOf(line));
    }

    private String readLine(BufferedReader reader) throws IOException {
        StringBuilder line = new StringBuilder();
        String next;

        do {
            next = reader.readLine();
            if (next == null) return null;
            line.append(next).append(System.lineSeparator());
        } while (next.lastIndexOf('[') > next.lastIndexOf(']'));

        return line.toString();
    }

    private boolean isDigit(CharSequence s, int i) {
        char c = s.charAt(i);
        return c >= '0' && c <= '9';
    }

    private int getKeyOf(String line) {
        // find the first ] (e.g. at the end of [INFO])
        // and read the first number after it
        int start = line.indexOf(']');
        while (!isDigit(line, start)) start++;

        int end = start;
        while (isDigit(line, end)) end++;

        return Integer.parseInt(line.substring(start, end));
    }
}

आपके पास कुछ विकल्प हैं।

सबसे अच्छा एक आईओओ एक व्युत्पन्न शब्दकोष का उपयोग करना होगा। इसका मतलब है कि प्रत्येक खोजशब्द x के लिए कम से कम एक लॉग में जो आप इसे रखे सभी लॉग के संदर्भ को संग्रहीत करते हैं। लेकिन जैसा कि आप पहले से ही इस कार्य पर एक सप्ताह बिता चुके हैं, मैं उस चीज़ का उपयोग करने के लिए सलाह देता हूं जो पहले से मौजूद है और ठीक उसी तरह है कि: लोचदार खोज आप वास्तव में पूर्ण ELK स्टैक (लोचदार खोज, लॉग्स्टाश, किबााना - मुख्य रूप से लॉग के लिए डिज़ाइन) का उपयोग कर सकते हैं ताकि लॉग को भी पार्स कर सकें, क्योंकि आप कॉन्फ़िग फ़ाइल में केवल एक रीजक्स अभिव्यक्ति डाल सकते हैं। आपको केवल एक बार फ़ाइलों को अनुक्रमणित करने की आवश्यकता होगी और खोजों को कुछ मिलीसेकंड के रूप में तेज़ मिलेगा

यदि आप वास्तव में ऊर्जा बर्बाद करना चाहते हैं और सर्वोत्तम समाधान के लिए नहीं जाना चाहते हैं, तो आप लॉग को फ़िल्टर करने के लिए हॉप पर मानचित्र-कम का उपयोग कर सकते हैं। लेकिन यह ऐसा कार्य नहीं है जहां नक्शा कम करना इष्टतम है और यह एक हैक की तरह अधिक होगा।





awk