ट्रैक
/
Go
Go
/
अभ्यास
/
लॉग फाइलें पार्स कीजिए
लॉग फाइलें पार्स कीजिए

लॉग फाइलें पार्स कीजिए

सीखने का अभ्यास

परिचय

Go में रेगुलर एक्सप्रेशन के लिए regexp पैकेज सहायता देता है।

सिंटैक्स

स्वीकार किए जाने वाले रेगुलर एक्सप्रेशन का सिंटैक्स वही सामान्य सिंटैक्स है जो Perl, Python और दूसरी भाषाओं में इस्तेमाल होता है।

खोज पैटर्न और इनपुट टेक्स्ट, दोनों को UTF-8 के रूप में पढ़ा जाता है।

जब स्ट्रिंग बनाने के लिए बैकटिक (`) का इस्तेमाल किया जाता है, तो बैकस्लैश (\) का कोई खास अर्थ नहीं रहता और वे टैब \t या नई लाइन \n जैसे खास अक्षरों की शुरुआत नहीं दर्शाते:

"\t\n" // regular string literal with 2 characters: a tab and a newline
`\t\n`// raw string literal with 4 characters: two backslashes, a 't', and an 'n'

इसी वजह से रेगुलर एक्सप्रेशन बनाने के लिए बैकटिक का इस्तेमाल करना बेहतर होता है, क्योंकि इससे बैकस्लैश को एस्केप करने की ज़रूरत नहीं पड़ती:

"\\" // string with a single backslash
`\\` // string with 2 backslashes

पैटर्न कंपाइल करना - RegExp टाइप

किसी रेगुलर एक्सप्रेशन का इस्तेमाल करने के लिए सबसे पहले हमें स्ट्रिंग पैटर्न कंपाइल करना होता है। यहाँ कंपाइल करने का मतलब है रेगुलर एक्सप्रेशन के स्ट्रिंग पैटर्न को लेकर उसे एक ऐसे अंदरूनी रूप में बदलना जिसके साथ काम करना आसान हो। हर पैटर्न को हमें सिर्फ एक बार कंपाइल करना होता है, उसके बाद हम रेगुलर एक्सप्रेशन के कंपाइल किए गए रूप का कई बार इस्तेमाल कर सकते हैं। regexp.Regexp टाइप एक कंपाइल किए गए रेगुलर एक्सप्रेशन को दर्शाता है। हम regexp.Compile फंक्शन की मदद से किसी स्ट्रिंग पैटर्न को regexp.Regexp में कंपाइल कर सकते हैं। अगर कंपाइल करना नाकाम रहे, तो यह फंक्शन nil और एक एरर लौटाता है:

re, err := regexp.Compile(`(a|b)+`)
fmt.Println(re, err) // => (a|b)+ <nil>
re, err = regexp.Compile(`a|b)+`)
fmt.Println(re, err) // => <nil> error parsing regexp: unexpected ): `a|b)+`

MustCompile फंक्शन Compile का एक सुविधाजनक विकल्प है:

re = regexp.MustCompile(`[a-z]+\d*`)

इस फंक्शन का इस्तेमाल करने पर एरर संभालने की ज़रूरत नहीं पड़ती।

Caution

MustCompile का इस्तेमाल तभी करना चाहिए जब हमें पक्का पता हो कि पैटर्न कंपाइल होगा, वरना प्रोग्राम पैनिक कर जाएगा।

रेगुलर एक्सप्रेशन के मेथड

Regexp में 16 मेथड हैं जो किसी रेगुलर एक्सप्रेशन से मैच करते हैं और मैच हुए टेक्स्ट की पहचान करते हैं। इनके नाम इस रेगुलर एक्सप्रेशन से मैच होते हैं:

Find(All)?(String)?(Submatch)?(Index)?
  • अगर All मौजूद है, तो मेथड पूरे एक्सप्रेशन के लगातार, बिना ओवरलैप वाले मैच ढूँढता है।
  • अगर String मौजूद है, तो आर्गुमेंट एक स्ट्रिंग होता है; वरना वह बाइट का स्लाइस होता है; रिटर्न वैल्यू ज़रूरत के मुताबिक बदल दी जाती हैं।
  • अगर Submatch मौजूद है, तो रिटर्न वैल्यू एक स्लाइस होती है जो एक्सप्रेशन के एक के बाद एक आने वाले सबमैच बताती है।
  • अगर Index मौजूद है, तो मैच और सबमैच इनपुट स्ट्रिंग के भीतर बाइट इंडेक्स के जोड़ों से पहचाने जाते हैं।

इसके अलावा इनके लिए भी मेथड हैं:

  • रेगुलर एक्सप्रेशन के मैचों को रिप्लेसमेंट स्ट्रिंग से बदलना, और
  • रेगुलर एक्सप्रेशन से अलग किए गए स्ट्रिंग को बाँटना।

कुल मिलाकर, regexp पैकेज 40 से अधिक फंक्शन और मेथड परिभाषित करता है। नीचे हम कुछ मेथड के इस्तेमाल का उदाहरण देखेंगे। इन और दूसरे फंक्शन के ब्यौरे के लिए API दस्तावेज़ देखिए।

MatchString के उदाहरण

MatchString मेथड बताता है कि किसी स्ट्रिंग में रेगुलर एक्सप्रेशन का कोई मैच है या नहीं।

re = regexp.MustCompile(`[a-z]+\d*`)
b = re.MatchString("[a12]")       // => true
b = re.MatchString("12abc34(ef)") // => true
b = re.MatchString(" abc!")       // => true
b = re.MatchString("123 456")     // => false

FindString के उदाहरण

FindString मेथड एक स्ट्रिंग लौटाता है जिसमें रेगुलर एक्सप्रेशन के सबसे बाएँ मैच का टेक्स्ट होता है।

re = regexp.MustCompile(`[a-z]+\d*`)
s = re.FindString("[a12]")       // => "a12"
s = re.FindString("12abc34(ef)") // => "abc34"
s = re.FindString(" abc!")       // => "abc"
s = re.FindString("123 456")     // => ""

FindStringSubmatch के उदाहरण

FindStringSubmatch मेथड स्ट्रिंग का एक स्लाइस लौटाता है जिसमें रेगुलर एक्सप्रेशन के सबसे बाएँ मैच का टेक्स्ट और उसके सबएक्सप्रेशन के मैच (अगर हों) होते हैं। इसका इस्तेमाल कैप्चरिंग ग्रुप से मैच करने वाली स्ट्रिंग की पहचान करने के लिए किया जा सकता है। nil रिटर्न वैल्यू का मतलब है कि कोई मैच नहीं मिला।

re = regexp.MustCompile(`[a-z]+(\d*)`)
sl = re.FindStringSubmatch("[a12]")       // => []string{"a12","12"}
sl = re.FindStringSubmatch("12abc34(ef)") // => []string{"abc34","34"}
sl = re.FindStringSubmatch(" abc!")       // => []string{"abc",""}
sl = re.FindStringSubmatch("123 456")     // => <nil>

ReplaceAllString के उदाहरण

re.ReplaceAllString(src,repl) मेथड src की एक कॉपी लौटाता है, जिसमें रेगुलर एक्सप्रेशन re के मैचों को रिप्लेसमेंट स्ट्रिंग repl से बदल दिया जाता है।

re = regexp.MustCompile(`[a-z]+\d*`)
s = re.ReplaceAllString("[a12]", "X")       // => "[X]"
s = re.ReplaceAllString("12abc34(ef)", "X") // => "12X(X)"
s = re.ReplaceAllString(" abc!", "X")       // => " X!"
s = re.ReplaceAllString("123 456", "X")     // => "123 456"

Split के उदाहरण

re.Split(s,n) मेथड टेक्स्ट s को एक्सप्रेशन से अलग किए गए सबस्ट्रिंग में बाँटता है और उन एक्सप्रेशन मैचों के बीच आने वाले सबस्ट्रिंग का स्लाइस लौटाता है। गिनती n तय करती है कि सबसे ज़्यादा कितने सबस्ट्रिंग लौटाने हैं। अगर n<0 है, तो मेथड सारे सबस्ट्रिंग लौटाता है।

re = regexp.MustCompile(`[a-z]+\d*`)
sl = re.Split("[a12]", -1)      // => []string{"[","]"}
sl = re.Split("12abc34(ef)", 2) // => []string{"12","(ef)"}
sl = re.Split(" abc!", -1)      // => []string{" ","!"}
sl = re.Split("123 456", -1)    // => []string{"123 456"}

निर्देश

यह अभ्यास लॉग फाइलों की पार्सिंग पर केंद्रित है।

हाल ही में हुई एक सुरक्षा समीक्षा के बाद आपसे कहा गया है कि आप संगठन की संग्रहित लॉग फाइलों को साफ करें।

फंक्शनों को दी जाने वाली सभी स्ट्रिंग के लिए यह गारंटी है कि वे null नहीं होंगी और उनके शुरू या अंत में कोई स्पेस नहीं होगा।

1. विकृत लॉग लाइनों की पहचान कीजिए

आपको अंदाज़ा लगाना है कि आपके संग्रह में कितनी लॉग लाइनें मौजूदा मानकों का पालन नहीं करतीं। आप मानते हैं कि एक आसान जाँच से पता चल जाता है कि कोई लॉग लाइन मान्य है या नहीं। मान्य माने जाने के लिए लाइन की शुरुआत इनमें से किसी एक स्ट्रिंग से होनी चाहिए:

  • [TRC]
  • [DBG]
  • [INF]
  • [WRN]
  • [ERR]
  • [FTL]

IsValidLine फंक्शन बनाइए, जो स्ट्रिंग के मान्य न होने पर false लौटाए और मान्य होने पर true।

IsValidLine("[ERR] A good error here")
// => true
IsValidLine("Any old [ERR] text")
// => false
IsValidLine("[BOB] Any old text")
// => false

2. लॉग लाइन को बाँटिए

संगठन में एक नई टीम जुड़ी है, और आप देखते हैं कि उसकी लॉग फाइलें "फील्ड" के लिए एक अजीब सेपरेटर इस्तेमाल करती हैं। कोलन ":" जैसी किसी समझदार चीज़ की जगह वे "<--->" या "<=>" जैसी स्ट्रिंग इस्तेमाल करती हैं (क्योंकि वह ज़्यादा सुंदर लगती है)। वास्तव में, वे ऐसी कोई भी स्ट्रिंग इस्तेमाल कर सकती हैं, जिसका पहला अक्षर "<" हो, अंतिम अक्षर ">" हो, और बीच में "~", "*", "=" तथा "-" में से किसी भी अक्षर का कोई भी मेल हो।

SplitLogLine फंक्शन बनाइए, जो एक लाइन लेता है और स्ट्रिंग का ऐरे लौटाता है, जिनमें से हर एक में एक फील्ड होता है।

SplitLogLine("section 1<*>section 2<~~~>section 3")
// => []string{"section 1", "section 2", "section 3"},

3. उद्धरण चिह्नों वाले टेक्स्ट में password रखने वाली लाइनों की गिनती कीजिए

टीम को उद्धरण चिह्नों वाले टेक्स्ट में पासवर्ड के उल्लेख के बारे में जानना है, ताकि उन्हें हाथ से जाँचा जा सके।

CountQuotedPasswords फंक्शन बनाइए, जो इस बात का अंदाज़ा दे कि हाथ से जाँचने का काम कितना बड़ा हो सकता है।

उन लॉग लाइनों को पहचानिए जिनमें "password" स्ट्रिंग, जो बड़े या छोटे अक्षरों के किसी भी मेल में हो सकती है, उद्धरण चिह्नों के बीच आती है। आपको इस बात का ध्यान रखना चाहिए कि उद्धरण चिह्नों के बीच "password" से पहले और बाद में कुछ और भी हो सकता है। हर लाइन में ज़्यादा से ज़्यादा दो उद्धरण चिह्न होंगे।

इस रूटीन को दी जाने वाली लाइनें कार्य 1 में बताए अनुसार मान्य हों या न हों। चाहे वे मान्य हों या न हों, हम उन पर एक ही तरह से काम करते हैं।

lines := []string{
    `[INF] passWord`, // contains 'password' but not surrounded by quotation marks
    `"passWord"`,  // count this one
    `[INF] User saw error message "Unexpected Error" on page load.`, // does not contain 'password'
    `[INF] The message "Please reset your password" was ignored by the user`, // count this one
}
// => 2

4. लॉग से अनचाहे टेक्स्ट हटाइए

आपने देखा है कि लॉग पर पहले चरण की प्रोसेसिंग "end-of-line" टेक्स्ट को, जिसके ठीक बाद बिना किसी स्पेस के एक लाइन नंबर आता है, पूरे लॉग में बिखेरती रही है।

RemoveEndOfLineText फंक्शन बनाइए, जो एक स्ट्रिंग लेता है, उसमें से "end-of-line" वाला टेक्स्ट हटाता है और एक साफ स्ट्रिंग लौटाता है।

जिन लाइनों में "end-of-line" टेक्स्ट नहीं है, उन्हें जैसी हैं वैसी ही लौटाना है।

बस "end-of-line" स्ट्रिंग हटा दीजिए। स्पेस को ठीक करने की कोशिश न कीजिए।

RemoveEndOfLineText("[INF] end-of-line23033 Network Failure end-of-line27")
// => "[INF]  Network Failure "

5. लाइनों पर यूज़र नेम के साथ टैग लगाइए

आपने देखा है कि कुछ लॉग लाइनों में ऐसे वाक्य होते हैं, जिनमें किसी यूज़र का उल्लेख होता है। इन वाक्यों में हमेशा "User" स्ट्रिंग होती है, जिसके बाद एक या ज़्यादा स्पेस अक्षर और फिर एक यूज़र नेम आता है। आप ऐसी लाइनों पर टैग लगाने का फैसला करते हैं।

TagWithUserName नाम का फंक्शन बनाइए, जो लॉग लाइनों पर यह काम करता है:

  • जिन लाइनों में "User " स्ट्रिंग नहीं है, वे जैसी हैं वैसी रहती हैं।
  • जिन लाइनों में "User " स्ट्रिंग है, उन लाइनों के शुरू में [USR] और उसके बाद यूज़र नेम लगाइए।

उदाहरण के लिए:

result := TagWithUserName([]string{
    "[WRN] User James123 has exceeded storage space.",
	"[WRN] Host down. User   Michelle4 lost connection.",
	"[INF] Users can login again after 23:00.",
	"[DBG] We need to check that user names are at least 6 chars long.",
})
// => []string {
//  "[USR] James123 [WRN] User James123 has exceeded storage space.",
//  "[USR] Michelle4 [WRN] Host down. User   Michelle4 lost connection.",
//  "[INF] Users can login again after 23:00.",
//  "[DBG] We need to check that user names are at least 6 chars long."
// }

आप यह मान सकते हैं कि:

  • लॉग में यूज़र नेम के बाद कम से कम एक स्पेस अक्षर होता है।
  • हर लाइन में "User " स्ट्रिंग ज़्यादा से ज़्यादा एक बार आती है।
  • यूज़र नेम ऐसी स्ट्रिंग होती हैं, जो खाली नहीं होतीं और जिनमें कोई स्पेस नहीं होता।
GitHub के ज़रिए संपादित करें यह लिंक एक नई विंडो या टैब में खुलता है
Go Exercism

लॉग फाइलें पार्स कीजिए शुरू करने के लिए तैयार हैं?

Exercism पर साइन अप कीजिए और Go को 34 कॉन्सेप्ट165 अभ्यास तथा असली इंसानों से मिलने वाली मेंटरिंग के साथ सीखिए और उसमें महारत हासिल कीजिए, वह भी बिल्कुल मुफ्त।