ארכיון

ארכיון הכותב

Chainsaw Session at ALT.NET tools meeting

12 פברואר, 2010 רון קליין אין תגובות

לפני שבועיים בערך הייתי במפגש של קבוצת ALT.NET ישראל.
מה זה ALT.NET? הנה העקרון הכללי:

We are a self-organizing, ad-hoc community of developers bound by a desire to improve ourselves, challenge assumptions, and help each other pursue excellence in the practice of software development.

(מתוך אתר ALT.NET)
בקיצור, כן מתכנתים בדוט נט, אבל לא עם הראש בקיר (ובעיקר לא "כי ככה אמרו במיקרוסופט"). כן להיפגש, אבל לא בהרצאות מובנות, אלא בשיחה פתוחה.

המפגש שבו השתתפתי עסק יותר בכלים שונים שיכולים ללוות את המתכנת או לשנות את צורת העבודה שלו. אוהד ישראלי מתאר את זה בפוסט משלו. תודה לחן אגוזי על הארגון ולחברת Delver על האירוח :-)

אני נתתי session על Chainsaw, מתוך כוונה ברורה "להפיץ את הבשורה" ולאפשר למתכנתים נוספים להכיר את ה viewer החביב הזה. בהפסקה נגשו אלי פה ושם אנשים שאמרו שזה באמת מחדש להם והם ינסו את זה. אשריני. אפשר לצפות בוידאו, אבל בעיקר שומעים אותי, וגם זה בקושי. אז הנה, למיטיבי השמיעה שביניכם:

בהזדמנות אני אוסיף לפוסט את המצגת ואת קבצי המקור.

ניסור נעים!

קטגוריות:תכנות תגיות:,

We're Hiring

החברה שבה אני עובד, אקסייטס מדיה, מגייסת בימים אלה עובדים/ות למשרות פיתוח.

בגדול מדובר בשלוש משרות פיתוח:

  1. Server Side Developer
  2. Web Developer
  3. Junior Developer

לתיאור המשרות ולפרטים נוספים – לחצו כאן.

קטגוריות:תכנות תגיות:

Locking by Decorator

בפוסט הזה:

  • הקדמה
  • הגדרת החוזה
  • מימוש רגיל ללא נעילות
  • נעילה באמצעות Decorator
  • יישום

הקדמה

לא מזמן כתבתי פוסט שמציע להפריד בין לוגיקה של רכיב ללוגיקה של נעילה ע"י אבסטרקציה. בפוסט הזה אני מציע דרך נוספת לעטוף רכיב בנעילה, תוך שימוש ב Design Pattern שנקרא Decorator. המטרה היא שוב להפריד בין לוגיקת רכיב ללוגיקת נעילה, כדי ליצור קוד נקי יותר וגמיש יותר לפי צרכי הפרויקט.

הגדרת החוזה

כדי לא להסתבך יותר מדי, אני אמשיך את הרעיון מהפוסט ההוא, ולכן דוגמת ה Counter תלווה אותנו גם כאן. נמיר את ה Counter שלנו מ class ל interface באופן הבא:

public interface ICounter
{
    void Hit();
    int Current { get; }
}

מימוש רגיל ללא נעילות

המימוש הרגיל והמיידי ל interface הנ"ל:

public class Counter : ICounter
{
    private int counter = 0;

    public void Hit()
    {
        ++counter;
    }

    public int Current
    {
        get { return counter; }
    }        
}

עד כאן טוב ויפה, אבל המימוש הוא כמובן לא thread-safe. עם זאת, הלוגיקה שבמימוש הזה ברורה וקריאה, ונוכל לכתוב unit tests שמתייחסים למימוש הזה בצורה פשוטה ומהירה. זה יתרון חשוב.

נעילה באמצעות Decorator

קצת הקדמה: מה זה Decorator?

אז ככה: בגדול, Decorator זה Design Pattern שמתייחס ל interface-ים ולהוספת פונקציונליות בזמן ריצה, לא על ידי ירושה. אפשר לקרוא עוד בויקיפדיה. במקרה שלפנינו, נוסיף פונקציונליות של נעילה על המימוש של ICounter. או, בניסוח אחר: "נקשט" את הפונקציונליות של Counter בנעילה, אבל נשמור על ה interface. כלומר החוזה נשמר אבל נקבל עוד משהו במימושים.

ניגש לקוד עצמו של ה Decorator שלנו:

public class CounterLockDecorator : ICounter
{
    private readonly ICounter core;
    private readonly object syncObject = new object();

    public CounterLockDecorator(ICounter coreCounter)
    {
        if (null == coreCounter)
            throw new ArgumentNullException("coreCounter");

        core = coreCounter;
    }

    public void Hit()
    {
        lock (syncObject)
        {
            core.Hit();
        }
    }

    public int Current
    {
        get
        {
            lock (syncObject)
            {
                return core.Current;
            }
        }
    }
}

כפי שניתן לראות, ה Decorator מממש בעצמו את החוזה ICounter. אבל הוא לא עושה את כל העבודה – הוא מצפה לקבל בקונסטרקטור שלו מימוש קיים ל ICounter ו"מגלגל" אליו את הקריאות לפי הצורך. כך, למשל, במימוש של המתודה Hit: ה Decorator רק עוטף בנעילה את הקריאה למתודת ה Hit של המימוש הקיים שהוא מחזיק.

יישום

הקריאה ל Counter הרגיל, ללא נעילות, יכולה להתבצע כך:

ICounter myCounter = new Counter();

ואם רוצים את הפונקציונליות של הנעילות, נוכל לקבל ICounter באופן הבא:

ICounter myCounter = new CounterLockDecorator(new Counter());

סיכום

גם בפוסט הזה הראיתי כיצד ניתן להפריד בין הלוגיקה של הרכיב לבין הנעילות שלו. יש להפרדה הזו גם יתרונות וגם חסרונות. היתרונות העיקריים הם קוד גמיש וטסטבילי (כלומר כזה שניתן לבדיקות בצורה קלה). גם הקוד של הרכיב עצמו קריא וברור. עם זאת, יש אי אילו חסרונות להפרדה הזו: פתאום יש שלושה קבצים לתחזק (החוזה, המימוש הרגיל וה decorator) במקום אחד, והקריאה הופכת להיות קצת יותר מורכבת (אפשר לפתור את זה עם factory). יש פתרון נוסף לעניין הזה, והוא שימוש ב dynamic proxy. אם יהיה לי זמן אני ארחיב על זה, אבל הרעיון הכללי הוא ליצור את ה Decorator בזמן ריצה באמצעות הכלים הסטנדרטיים של דוט נט כמו CodeDOM או באמצעות פריימוורק שהוא קצת יותר high level כמו Dynamic Proxy של Castle.

אגב, אפשר להשתמש בנעילה האבסטרקטית שהצעתי בפוסט ההוא בתוך ה Decorator וכך "להרוויח" עוד קצת גמישות בבחירת מנגנון הנעילה עצמו, אבל זה כבר עניין אחר :-)

כל הקוד נמצא כאן, ושיהיה קישוט נעים!

Tip: Setting a startup project in the .sln file

הכנתי פרויקט Lib מסוג Class Library והכנתי פרויקט Demo (שמשתמש ב Lib) מסוג Console Application, ושניהם באותו solution.
שמרתי הכל, והגדרתי שה Demo יהיה ה startup project. הגיוני.
ועכשיו רציתי לשים את הכל כדוגמה לפוסט, אז כיווצתי הכל ל zip.

רק שניה, לפני שאני מעלה את זה, רק כדי לוודא שהכל עובד, אני פותח את הזיפ בתיקיה חדשה וריקה, ומשם פותח את ה Visual Studio 2008 ולוחץ F5 כדי להריץ… ופתאום הוא בוכה שאי אפשר להריץ Class Library, ואני הרי זוכר שממש לפני דקה הגדרתי שה startup project יהיה ה Demo. :mad:

טוב, בטח אני לא הראשון שנתקל בזה, אז קדימה לגגל!
הגעתי (איך לא) לדיון דומה ב stackoverflow.com, וגם לפוסט בנושא בבלוג של Arian Kulp.
מה מסתבר? שההגדרה של ה startup project לא נשמרת ב sln, אלא ב suo. :shock:
יש דרך לעקוף את זה:
פותחים את קובץ ה sln באיזה text editor כמו notepad (אני אישית מעריץ נלהב של NPP), ושם יש שורות כמו אלו:

Microsoft Visual Studio Solution File, Format Version 10.00
# Visual Studio 2008
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lib", "Lib\Lib.csproj", "{F959D3D9-052F-4C62-B957-BF6459CB2209}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Demo", "Demo\Demo.csproj", "{852E3624-B5EB-40DF-A2EE-481037C8F47B}"
EndProject

פשוט מעבירים את החלק של Project…EndProject של הפרוייקט הרצוי להיות הראשון. בהמשך לדוגמה הזו, המצב הרצוי הוא:

Microsoft Visual Studio Solution File, Format Version 10.00
# Visual Studio 2008
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Demo", "Demo\Demo.csproj", "{852E3624-B5EB-40DF-A2EE-481037C8F47B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lib", "Lib\Lib.csproj", "{F959D3D9-052F-4C62-B957-BF6459CB2209}"
EndProject

וזה עושה את העבודה.

שימושי כשרוצים להוסיף פרויקט דוגמה לפוסט, ו/או בפרויקטים של קוד פתוח ברשת.
תוכלו לנסות בעצמכם :-)

קטגוריות:תכנות תגיות:,

From lock to abstract locker

6 ינואר, 2010 רון קליין 2 תגובות

כמו שמשתמע מהכותרת של הפוסט הזה, לא חייבים לנעול דווקא עם lock (כלומר נעילה שמבוססת על Monitor).
יש נעילות נוספות (למשל, Spin-Lock), ולכן אני מציע כאן אבסטרקציה לנעילה.

במילים אחרות, במקום הקוד הזה:

lock(locker)
{
    // do something
}

אני מציע משהו כזה:

locker.Enter();
// do something
locker.Exit();

אלא שזה קצת מעצבן, ולכן אפשר לשכלל את זה אם נשתמש ב IDisposable וניצור משהו יותר נחמד עם ה using שהוא סוג של מאקרו בדוט נט:

using(locker.EnterLock())
{
    // do something
}

אז בפוסט הזה יש:

  • קצת אבסטרקציה
  • קצת מוטיבציה
  • קוד בסיס
  • 2 ירושות
  • וקצת אינטגרציה :-)

קצת מוטיבציה

מתי זה שימושי?
-בעיקר אם נרצה לכתוב class שיכול להיות שימושי גם בסביבה שהיא single-threaded וגם בסביבה שהיא multi-threaded.

קצת קוד בסיס

ניגש לקוד עצמו. הנה ה base class עם תוספת פנימית בשביל ה using הנכסף:

using System;

public abstract class LockerBase
{
    public abstract void Enter();
    public abstract void Exit();

    public class DisposableLocker : IDisposable
    {
        private readonly LockerBase locker;

        public DisposableLocker(LockerBase locker)
        {
            this.locker = locker;
        }

        public void Dispose()
        {
            locker.Exit();
        }
    }

    public DisposableLocker EnterLock()
    {
        Enter();
        return new DisposableLocker(this);
    }
}

המתודות Enter ו Exit הן אבסטרקטיות, וכל מי שירצה לרשת מה base class הזה יצטרך לממש אותן.
אגב, אני לא מת על ירושה (לא בתכנות, לפחות), אבל כאן זה נראה יותר מתאים, כי יש איזה state לשמור עליו.

ירושה – המימוש ה"רגיל"

בואו נראה למשל, את המימוש המתבקש לנעילה "שגרתית":

using System.Threading;

public class MonitorLocker : LockerBase
{
    private readonly object syncRoot;

    public MonitorLocker(object syncObject)
    {
        syncRoot = syncObject;
    }

    public MonitorLocker() : this(new object())
    {
    }

    public override void Enter()
    {
        Monitor.Enter(syncRoot);
    }

    public override void Exit()
    {
        Monitor.Exit(syncRoot);
    }        
}

בקונסטרקטור נשים אובייקט שתפקידו להיות המשאב המשותף שעליו מבוססים השיתוף והנעילה.

ירושה – המימוש הריק

ועכשיו לקוד קצת אחר: class שלמעשה לא נועל. מעין dummy class שרק שומר על הפונקציונליות כלפי חוץ:

public class DummyLocker : LockerBase
{
    public override void Enter()
    {
    }

    public override void Exit()
    {
    }
}

בעקרון ה class הזה לא ישבור קומפילציה, אבל הוא לא באמת נועל כלום. זה ה dummy שלנו למקרה שמריצים את הקוד בסביבה שהיא single-threaded.

קצת אינטגרציה

אחרי כל הקוד הזה, נראה שימוש לאבסטרקציה הזו. נניח שיש לנו class בשם Counter. כל מה שיש שם זה מתודה בשם Hit ופרופרטי בשם Current.
וכל מה שצריך לקרות זה שכל קריאה ל Hit מעלה ב 1 את הערך של Current (שערכו ההתחלתי הוא 0).
אנחנו יודעים מראש שיהיו מצבים שבהם ה class הזה יהיה שימושי בסביבה שהיא single-threaded וכן בסביבה שהיא multi-threaded. לכן נשתמש בנעילה האבסטרקטית:

public class Counter
{
    private readonly LockerBase locker;
    private int counter = 0;

    public Counter(LockerBase locker)
    {
        this.locker = locker;
    }

    public void Hit()
    {
        using (locker.EnterLock())
        {
            ++counter;
        }

    }

    public int Current
    {
        get
        {
            using (locker.EnterLock())
            {
                return counter;
            }
        }
    }
}

הקוד די פשוט: כל התייחסות ל counter הפנימי "עטופה" בנעילה, אבל אנחנו לא יודעים מה יהיה המנעול שאיתו נעבוד.
המנעול עצמו יכנס ל class דרך הקונסטרקטור (ולזה קוראים Dependency Injection).
כך, למשל, ניצור instance של Counter לסביבה שהיא single-threaded (כלומר ללא צורך בנעילות):

Counter myCounter = new Counter(new DummyLocker());

אבל, כדי לא לעצבן את המתכנת שישתמש ב class הזה, אולי כדאי שנוסיף overload עם ה dummy-locker שלנו כברירת מחדל לקונסטרקטור ריק:

public Counter() : this(new DummyLocker())
{
}

ואז השימוש יהיה קל יותר:

Counter myCounter = new Counter();

נוכל גם להוסיף מתודה סטאטית שתפקידה ליצור instance שהוא thread-safe באופן הבא:

public static Counter Synchronized()
{
    return new Counter(new MonitorLocker());
}

והקריאה:

Counter myCounter = Counter.Synchronized();

סיכום

מה שהצעתי כאן זה אבסטרקציה למנגנון הנעילה, תוך שימוש במאקרו של using כדי לכתוב קוד שמתנהג קרוב ככל האפשר ל lock המוכר.
האבסטרקציה הזו שימושית אם יודעים מראש שיש איזה class שיהיה שימושי גם בסביבה שהיא multi-threaded וגם בסביבה שהיא single-threaded.
עם זאת, יש דרכים נוספות להשיג את התוצאה הזו.
למשל, לכתוב interface ל Counter וליצור decorator שהוא יעטוף כל פעולה בנעילה, או dynamic proxy שיעשה את זה באופן אוטומטי. אולי אני ארחיב על זה בהזדמנות.
בכל מקרה, מה שאנחנו משיגים כאן זו הפרדה של הלוגיקה של הקוד עצמו מהלוגיקה של הנעילה, וזה נחמד לכשעצמו, וגם נותן לנו יתרון משמעותי אם נרצה לכתוב unit testing לקוד הזה.

ניתן להוריד את הקוד של הפוסט הזה מהלינק הזה.

log4net – המדריך למלגלג (חלק 3 – למצוא viewer נורמלי)

12 דצמבר, 2009 רון קליין תגובה אחת

בפוסט הזה:

  • למה צריך viewer ללוג
  • קריטריונים ל viewer נורמלי
  • איזה viewerים קיימים
  • Chainsaw – ה viewer המועדף עלי (מאיפה מורידים, איך מגדירים וכו')

למה צריך Viewer ללוג

אז עשית try/catch (ואולי גם finally), וכתבת ללוג את ה exception. נפלא.
עכשיו מה?
הרי עכשיו, אם אתה יודע שיש איזו שגיאה אי שם בלוג, צריך להתחיל לבדוק מה הסיפור שלה, מה גרם לה להתרחש, מה ומה ומה.
לכל error יש סיפור מאחוריו, עלילה שלמה. בשביל זה צריך viewer – תוכנה שבעזרתה ניתן לראות את הלוג ולנסות להבין את הסיפור.

Log Viewer זו תוכנה די יבשה, שמציגה שורות על גבי שורות, כל שורה היא הודעה ללוג.
הנה כמה תמונות מסך מ viewers שונים:

log4view Kiwi Log Viewer Chainsaw

קריטריונים ל Viewer

כל viewer חייב מערת סינון (filter), כך שמתוך כל ההודעות בלוג יוצגו רק אלו שעונות לתנאי/ם מסויימ/ים.
יהיה נחמד אם ב viewer תהיה גם אפשרות "לקפוץ" להודעה הבאה שמקיימת תנאי מסויים (כמו Find או Search בעורכי טקסט)
יהיה מאוד נחמד לקבל צביעת שורות לפי תנאים מסויימים (לאו דווקא אותם תנאים של הסינון)
אז בואו נפרסם הודעת "דרושים" ל-viewer האידיאלי
לפרויקט עם log4net דרוש viewer לצפיה בלוג:
יודע לקרוא קבצי לוג, גם סגורים וגם פתוחים
יודע לתת חתכי "וגם" (and) – חובה
יודע לתת חתכי "או" (or) – חובה
יודע לתת חתכי טקסט חלקי (like) – חובה
יודע לבצע חיפוש (קפיצות) ע"פ הקריטריונים הנ"ל – יתרון
צביעת שורות על פי תנאים – יתרון
תיעוד טוב – יתרון

אז מה כבר קיים בשוק?

קודם כל, אפשר לכתוב הודעות לוג ל DB. נכון שזה חתיכת overhead לרשום הודעות לוג ל DB, אבל זה אפשרי ב live, ואפשר גם לקחת קובץ לוג, לקרוא אותו, ו"לשפוך" את ההודעות ל DB. מה זה נותן לנו? הרבה כוח לבצע חיתוכים כמה ואיך שאנחנו רוצים. כל מי שהתעסק פעם עם SQL יודע שאפשר ליצור שאילתות פשוטות בזמן קצר. ואפשר להעזר בכל designer כדי לכתוב שאילתות מורכבות יותר. גם ה SQL Management Studio שבא יחד עם MS-SQL-Server עובד יפה.
במילים פשוטות, אם רוצים, אז אפשר לבנות שאילתות מול DB ובאמצעותן לקבל כמעט את כל מה שרוצים. אמנם לא בצבעים יפים, אבל תכלס זה אפשרי ופרקטי.

כמו שסבתא שלי היתה אומרת: נו, וחוץ מזה?

או, אז באמת יש כמה דברים:

אתם מוזמנים לבדוק כל כלי בנפרד. אני בדקתי, ולמרות שמצאתי פיצ'רים חביבים פה ושם, רק Chainsaw עונה על הקריטריון הכי משמעותי מבחינתי: ביצוע חתכי "או". לדוגמה: אני מעוניין לצפות בכל הודעות הלוג שמופיעה בהן המילה "session" ו/או בהודעות הלוג שהגיעו מ class שנקרא "MailSender". כמה טריויאלי, כמה נחוץ, וכמה לא קיים כמעט בשום מקום.

שום מקום חוץ מ Chainsaw, כאמור. הפרויקט הזה, Chainsaw, הוא בכלל ב Java ולמען לוגים בפורמט של log4j. כן, כמו הרבה דברים שקשורים בדוט נט, גם כאן זה התחיל ב Java. אלא מה, הפעם אין שום מקבילה ראויה ל Chainsaw בדוט נט. כך שה look and feel אמנם קצת צולע, אבל הי, חכו תראו איזה מנוע שאילתות מגיע עם הקביים!

Chainsaw – ה Viewer המועדף עלי

הורדות והתקנות

1. הורדות: כדי להריץ את ה Chainsaw הלז צריך שיהיה לכם מותקן על המחשב JRE. וכמובן, צריך להוריד את המסור מהאתר הרשמי של Apache. אני ממליץ להוריד את גרסת ה Unix/Dos Standalone. ה Chainsaw חינמי ברשיון Apache גירסה 2.
2. הורדתם? יופי. עכשיו תתקינו. פשוט לעשות Unzip של גירסת ה Standalone לתיקיה מספיק נוחה לגישה בהארד דיסק. תסלחו לי על הנאיביות, אבל אצלי זה מותקן ב C:\chainsaw-bundle. פשוט ככה, כמו בימי DOS העליזים, כששיחקנו Doom ושאר משחיתי-נוער.
3. התקנתם? יופי, עכשיו תפעילו (פשוט תריצו את הקובץ chainsaw.bat) ומיד תקבלו ניג'וס ראשון "זאת הפעלה ראשונה שלך, בלה-בלה, לא מוגדרים receiver-ים". לא נורא, נגדיר ידנית (ראו תמונה).

ויש עוד ניג'וס כללי: החלון של ה bat – פשוט נמצא שם, מכוער במלוא תפארתו. לא חייבים להשלים עם המונומנט המכוער, אפשר להוריד Launcher ל Java ולעטוף הכל יפה יפה. אני ממליץ על jsmooth (עוד על זה בפוסט נפרד).

הגדרות ב log4net

זה נחמד שמותקן ה Chainsaw, אבל לא מספיק. כמו שציינתי, במקור ה Chainsaw מגיע מעולם ה Java, וזה אומר שהוא מצפה לקרוא את ההודעות בפורמט של log4j. אז כדי שה log4net יוציא הודעות כאלו, צריך לשנות קצת את ההגדרות. אם נתייחס לדוגמה מהפוסט הקודם, אז נשנה את קובץ ה config באופן הבא:

<log4net>
  <appender name="WowItsTheLog4jFileAppender" type="log4net.Appender.FileAppender">
    <file value="mylog.txt" />
    <appendToFile value="true" />
    <layout type="log4net.Layout.XmlLayoutSchemaLog4j" >
      <locationInfo value="true" />
    </layout>
  </appender>

  <root>
    <level value="DEBUG" />
    <appender-ref ref="WowItsTheLog4jFileAppender" />
  </root>
</log4net>

לפני שנחזור ל Chainsaw, נכתוב תוכנית קטנה ש"עובדת קשה" ונותנת הודעות ללוג מכל מיני classים ובת'רדים שונים, כולל exceptions ייזומים. בקיצור, תוכנית שעושה שמח. אני לא ארשום כאן את הקוד של התוכנית, כי זה לא מה שמעניין. מה שמעניין זה איך התוכנית כותבת ללוג ואיך משתמשים ב Chainsaw כדי לצפות בהודעות שמתקבלות. בכל מקרה, התוכנית מצורפת כולל כל ה source.

עובדים עם Chainsaw – למנסרים הידד

עכשיו נחזור ל Chainsaw שלנו. התוכנית שלנו שמרה את הודעות הלוג בקובץ. פשוט נגרור אותו לתוך ה Chainsaw, ואחרי כמה שניות טובות (תלוי כמה המחשב שלכם סוחב Java), הכל נפתח ומוצג. קודם כל, צריך להתרגל קצת למסך הראשי. החלק העיקרי מציג את הודעות הלוג עצמן, עם עמודות מתאימות. העץ שמשמאל מתאר את היררכיית הלוגרים בקונטקסט הנוכחי. לצערנו, גם הלוגרים של ה Chainsaw עצמו מוצגים שם :-( אלא מה, אפשר להעביר את הפוקוס ללוגר הרצוי, ואז יוצגו רק ההודעות הרלוונטיות במסך הראשי.

כדי לבצע שאילתות על ההודעות המוצגות, צריך להשתמש בתחביר של מנוע השאילתות של Chainsaw, ולהקליד באזור של Refine focus on. נטעם קצת ע"י דוגמאות:

MSG ~= 'email' מציג רק את ההודעות שמכילות את המילה email
MSG ~= 'email' && LOGGER ~= 'sender' רק הודעות שמכילות את המילה email ששם הלוגר שלהן מכיל את המילה sender
MSG ~= 'go' || MSG ~= 'before' מציג הודעות שמכילות את המילה go ו/או הודעות שמכילות את המילה before
( LOGGER == 'mylogger' && MSG ~= 'cat' ) || ( THREAD == 1234 ) מציג הודעותשהגיעו מת'רד מספר 1234 ו/או הודעות שמכילות את המילה cat שהגיעו מלוגר שנקרא mylogger
PROP.log4jid >= 400 מציג את כל ההודעות החל משורה 400
LEVEL >= 'warn' מציג רק הודעות מסוג Warn או גבוה ממנו (למשל Error יוצג, אבל Info לא יוצג)

שימו לב שיש רווח לפני ואחרי פתח/סגור סוגריים וכן לפני/אחרי אופרטורים וכד'. בקיצור, צריך רווחים, וה Chainsaw לא סולח ולא שוכח. אם משהו בתחביר השאילתה לא תקין, ה Chainsaw פשוט מתעלם ממנה, ולא מציג שום הודעת שגיאה. כזה הוא, לא משהו ביחס למשתמשים. שיהיה.

קבלת הודעות ב UDP

ב Chainsaw, כמו גם ב viewerים אחרים, יש אפשרות של קבלת הודעות לוג באמצעות תקשורת TCP וכן UDP. כלומר, כמו שה log4net כותב הודעות לוג לקובץ, הוא יכול לשלוח אותם בפרוטוקול TCP או UDP לאן שנרצה. וככה אפשר לצפות בהודעות תוך כדי ריצה של כמה תוכנות במקביל. כשעובדים בסביבה מבוזרת זה פיצ'ר מאוד חזק. אגב, ב UDP לא ניתן לשלוח הודעות לוג ארוכות. מה לעשות, ככה זה.

אז איך?

1. בכל תוכנה רלוונטית, מגדירים ב log4net שליחת הודעות לוג ב UDP:

<appender name="HeyLetsGoWithUdp" type="log4net.Appender.UdpAppender">
  <remoteAddress value="localhost" />
  <remotePort value="48899" />
  <layout type="log4net.Layout.XmlLayoutSchemaLog4j" >
    <locationInfo value="true" />
  </layout>
</appender>

2. מגדירים receiver ב Chainsaw שיאזין להודעות ב UDP באותו פורט שהגדרנו קודם. התמונות הבאות מסבירות את זה:

מוסיפים ומגדירים

זהו.

ועוד כמה דברים על ה Chainsaw

הסקירה כאן היא רק ברמה של היכרות, ויש עוד כמה דברים חביבים ב Chainsaw הפרחח:

אפשר לצבוע שורות לפי אותו מנוע שאילתות, כולל תיעדוף צביעה (במידה שאותה שורה עונה על שני תנאי צביעה שונים). זה פיצ'ר קצת חבוי, אבל מגיעים אליו די מהר.

ניתן לבצע Find

אפשר למיין את ההודעות (המיון הדיפולטי הוא סדר ההודעות כפי שנרשמו ללוג או כפי שהתקבלו ע"י ה Chainsaw, וזה לא בהכרח ה Timestamp שלהן)

הגדרות – מציע לכם לשנות בהגדרות שה Cyclic buffer size יהיה, נניח, 50000. ובאותו מקום אפשר גם להסיר את הסימון מ Confirm Exit.

ה Chainsaw יכול להיתקע בגלל מחסור בזכרון. כדי להתגבר על זה, רצוי להגדיר בקובץ bat הקצאת זיכרון גדולה יותר. פשוט להוסיף את הטקסט -Xms32m -Xmx1024m אחרי ה java. כך שהתוצאה היא

java -Xms32m -Xmx1024m -Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JLogger -classpath jakarta-oro-2.0.6.jar;jmdns.jar;log4j-1.3alpha-7.jar;log4j-chainsaw-2.0alpha-1.jar;log4j-optional-1.3alpha-7.jar;log4j-oro-1.3alpha-7.jar;log4j-smtp-1.3alpha-7.jar;log4j-xml-1.3alpha-7.jar;log4j-zeroconf.jar;xstream-1.1.2.jar org.apache.log4j.chainsaw.LogUI

סיכום

זהו, עד כאן לענייני Chainsaw ו log4net. יש, כמובן, כלים נוספים, ויש תשתיות לוג כאלו ואחרות (יש למשל את SmartInspect – אני לא מת על הקונספט שלהם, כי זה להכניס business logic לתוך תשתית הלוג, אבל שווה בדיקה). אני לא מתיימר לקבוע ש log4net טובה יותר מתשתיות אחרות. אבל אני כן חושב ש log4net היא תשתית מצויינת ללוג, עושה את העבודה בצורה טובה, מאפשרת הגדרות מתקדמות כמו סינונים, ומאפשרת הרחבות. בנוסף לכל, log4net היא open source, וזה אומר שגם אם יש באג, אפשר בקלות להכנס לקוד ולראות מה קורה גם בפנים. כדי לצפות בתוצרי הלוג אני משתמש ב Chainsaw, שהוא כלי מצוין לחתכים ולשאילתות, אבל מצריך קצת סבלנות בעבודה.

אם יש להשיג viewer טוב יותר ל log4net – תנו לי פינג.

ועד אז, לגלוג נעים!

קטגוריות:תכנות תגיות:,