จากมุมมองการออกแบบแนวทางปฏิบัติที่ดีที่สุดสำหรับการบันทึกคืออะไร [ปิด]


11

ฉันต้องการเพิ่มการบันทึกลงในแอปพลิเคชันที่ฉันกำลังทำงานอยู่ ฉันได้เพิ่มการบันทึกก่อนหน้านี้ไม่ใช่ปัญหาที่นี่

แต่จากมุมมองการออกแบบในภาษาเชิงวัตถุอะไรคือวิธีปฏิบัติที่ดีที่สุดสำหรับการบันทึกที่เป็นไปตาม OOP และรูปแบบ

หมายเหตุ:ฉันกำลังทำสิ่งนี้ใน C # ดังนั้นตัวอย่างใน C # ยินดีต้อนรับอย่างชัดเจน ฉันต้องการเห็นตัวอย่างใน Java และ Ruby


แก้ไข:ฉันใช้ log4net ฉันไม่รู้ว่าวิธีที่ดีที่สุดในการเสียบคืออะไร

คำตอบ:


6

แนวทางปฏิบัติที่ดีที่สุดที่ฉันอยากจะแนะนำคือการใช้log4jมากกว่าที่จะทำเอง (ซึ่งได้รับการย้ายจาก Java ไปยัง C # และ Ruby ดังนั้นจึงใช้กับทั้ง 3 ภาษาที่คุณสนใจ)

หากคุณอ่านหน้าคู่มือนั้นคุณจะค้นพบแนวทางปฏิบัติที่ดีอื่น ๆ อีกหลายประการ เช่นการมีน้ำหนักเบาสามารถกำหนดค่าได้นอกแอปพลิเคชันของคุณสามารถพลิกขึ้นและลงการบันทึกสำหรับส่วนต่าง ๆ ของแอปพลิเคชันของคุณได้อย่างอิสระเป็นต้น


5

ฉันทำงานที่ไหนเราเขียนแอปเดสก์ท็อป. NET จำนวนมาก โดยปกติเราจะใช้ 2 เหตุการณ์ในส่วนประกอบของเราหนึ่งรายการสำหรับการบันทึกข้อมูลและอีกรายการสำหรับการบันทึกข้อยกเว้น (แม้ว่าเรามักจะปล่อยให้ข้อยกเว้นเกิดฟองสบู่ขึ้นแทนที่จะยกเหตุการณ์แยกต่างหากขึ้นอยู่กับสถานการณ์) การใช้สถาปัตยกรรมนี้ห้องสมุดของเราไม่จำเป็นต้องรู้ว่าจะมีการใช้งานการบันทึกอย่างไรหรือมีการใช้จัดเก็บหรือจัดการข้อมูลอย่างไร จากนั้นเราให้แอปพลิเคชันจัดการกับกิจกรรมการบันทึกในลักษณะที่เหมาะสมกับแอปนั้น หลายปีที่ผ่านมาสถาปัตยกรรมนี้ทำให้เราเปลี่ยนจากการใช้การบันทึก MS Enterprise Library ไปยังองค์ประกอบการบันทึกของ BitFactory การเปลี่ยนแปลงที่ง่ายมาก


+1 สำหรับการใช้รูปแบบกิจกรรม / ผู้สังเกตการณ์: เปลี่ยนผู้สังเกตการณ์คุณเปลี่ยนการบันทึก
Matthieu M.

2

เนื่องจากคุณกำลังทำสิ่งนี้ใน C # ฉันขอแนะนำให้คุณดูที่ NLog และ ElMAH สามารถติดตั้งได้ง่ายมากโดยใช้ NUGET ฉันได้ใส่ลิงค์ไปยังลิงก์ด้านล่างเพื่อให้คุณได้รับข้อมูลเพิ่มเติม


2

โดยส่วนตัวแล้วฉันใช้กรอบการบันทึกของตัวเลือก (ในกรณีของฉันคือ Entlib เพราะฉันทำงานกับ. NET) และเขียน AOP สำหรับการบันทึก

จากนั้นคุณสามารถระบุวิธีการ / คุณสมบัติ / คลาส / เนมสเปซและเพิ่มการบันทึกได้โดยไม่ทำให้แหล่งที่มายุ่งเหยิง


มันฟังดูน่าสนใจมาก แต่ฉันมีการจองเกี่ยวกับสิ่งที่คุณจะสามารถเข้าสู่ระบบและวิธีการบันทึกข้อมูล (เช่นมากกว่า "เพียงแค่" เครื่องมือวิธีการ) ชอบที่จะเห็นตัวอย่างการทำงานของวิธีนี้เพื่อดูว่าสามารถและไม่สามารถทำได้ โดยเฉพาะอย่างยิ่งเมื่อฉันเพิ่งเริ่มใช้งานแอพใหม่และต้องการที่จะดูว่าฉันสามารถพกพาไปได้ไกลแค่ไหน
Marjan Venema

@marjan Venema: เอกสารคู่มือโพสต์คมมีตัวอย่างของมุมมองที่บันทึกการเข้า / ออกจากวิธีการ doc.sharpcrafters.com/postsharp/2.0/##PostSharp.chm/html/…ในกรณีของ Post sharp มันจะทอโค้ดจากแอ็ตทริบิวต์ลงในแหล่งที่มา ณ เวลาที่สร้างจึงไม่ส่งผลกระทบต่อประสิทธิภาพเหมือนกับที่คนอื่นทำ
Steven Evers

1

ระบบที่ฉันกำลังใช้งานอยู่ใช้สถาปัตยกรรมที่ขับเคลื่อนด้วยเหตุการณ์และการส่งข้อความเพื่อให้การกระทำส่วนใหญ่ในระบบของเราเป็นผลมาจากคำสั่งและพวกเขาส่งผลให้เกิดเหตุการณ์ (ในชั้นเรียน DTO ที่ถูกส่งมากกว่าเหตุการณ์ตัวแทนมาตรฐาน) เราแนบตัวจัดการเหตุการณ์โดยมีวัตถุประสงค์เพื่อจัดการการบันทึกเท่านั้น การออกแบบนี้ช่วยให้เราไม่ต้องทำซ้ำตัวเองและไม่ต้องแก้ไขรหัสที่มีอยู่เพื่อเพิ่ม / เปลี่ยนฟังก์ชั่น

นี่คือตัวอย่างของคลาสการบันทึกหนึ่งคลาสที่จัดการเหตุการณ์ทั้งหมดที่จะถูกบันทึกจากส่วนที่แคบของแอปพลิเคชันของเรา (ที่เกี่ยวข้องกับแหล่งเนื้อหาหนึ่งที่เรานำเข้ามา)

ฉันไม่จำเป็นต้องพูดว่านี่เป็นวิธีปฏิบัติที่ดีที่สุดเนื่องจากฉันเปลี่ยนใจในสิ่งที่และวิธีการเข้าสู่ระบบบ่อยครั้ง - และทุกครั้งที่ฉันต้องใช้บันทึกเพื่อวิเคราะห์ปัญหาฉันย่อมหาวิธีที่จะปรับปรุง ข้อมูลที่ฉันบันทึก

ฉันจะบอกว่าการบันทึกข้อมูลที่เกี่ยวข้อง (โดยเฉพาะอย่างยิ่งใน Ctrl-F / ค้นหาวิธีที่ค้นหาได้) เป็นส่วนที่สำคัญที่สุด

ส่วนที่สำคัญที่สุดที่สองจะได้รับรหัสการเข้าสู่ระบบออกจากตรรกะหลักของคุณ - มันสามารถทำให้วิธีการที่น่าเกลียดและระยะยาวและซับซ้อนมากได้อย่างรวดเร็ว

public class MctLogger :
    IEventHandler<StoryImported>,
    IEventHandler<StoryScanned>,
    IEventHandler<SourceDirectoryMissing>,
    IEventHandler<SourceDirectoryAccessError>,
    IEventHandler<CannotCreateScannedStoryDirectory>,
    IEventHandler<CannotReadStoryDocument>,
    IEventHandler<StorySkippedPastCutoff>,
    IEventHandler<StorySkippedDuplicateUniqueId>,
    IEventHandler<StorySkippedByFilter>
{

    public void Observe(StoryImported e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.StoryImported");
        log.Info("Story Unique ID: {Story.UniqueId}, Content ID: {ContentId}, Title: {Story.Headline}".SmartFormat(e));
    }

    public void Observe(StoryScanned e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.StoryScanned");
        log.Info("Story Unique ID: {Story.UniqueId}, File: {FilePath}, Title: {Story.Headline}".SmartFormat(e));
    }

    public void Observe(SourceDirectoryMissing e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.SourceDirectoryMissing");
        log.Error("Directory: " + e.Directory);
    }

    public void Observe(SourceDirectoryAccessError e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.SourceDirectoryAccessError");
        log.Error(e.Exception, "Exception: " + e.Exception.Message);
    }

    public void Observe(CannotCreateScannedStoryDirectory e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.CannotCreateScannedStoryDirectory");
        log.Error(e.Exception, "Directory: {Directory}, Exception: {Exception.Message}".SmartFormat(e));
    }

    public void Observe(CannotReadStoryDocument e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.CannotReadStoryDocument");
        if (e.Exception == null) {
            log.Warn("File: {FilePath}".SmartFormat(e));
        }
        else {
            log.Warn(e.Exception, "File: {FilePath}, Exception: {Exception.Message}".SmartFormat(e));
        }
    }

    public void Observe(StorySkippedPastCutoff e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.StorySkippedPastCutoff");
        log.Warn("Story Unique ID: {Story.UniqueId}, File: {FilePath}, Title: {Story.Headline}".SmartFormat(e));
    }

    public void Observe(StorySkippedDuplicateUniqueId e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.StorySkippedDuplicateUniqueId");
        log.Warn("Story Unique ID: {Story.UniqueId}, File: {FilePath}, Title: {Story.Headline}".SmartFormat(e));
    }

    public void Observe(StorySkippedByFilter e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.StorySkippedByFilter");
        log.Warn("Story Unique ID: {Story.UniqueId}, Reason: {Reason}, File: {FilePath}, Title: {Story.Headline}".SmartFormat(e));
    }
}

1

ตามที่คนอื่นพูดใช้log4jหรือlog4netกรอบการทำบันทึกที่สร้างขึ้นอย่างดี

ฉันมักจะไม่ชอบรหัสการบันทึกที่ได้รับในทางของตรรกะทางธุรกิจ Log4PostSharpนั่นเป็นเหตุผลที่ผมใช้ นั่นหมายความว่าฉันสามารถใช้Aspect Oriented Programmingเพื่ออธิบายวิธีการเช่นนี้:

[Log(LogLevel.Info, "Counting characters.")]
int CountCharacters(string arg) 
{
    return arg.Length;
}

หรือทุกวิธีในการชุมนุมเช่นนี้

[assembly: Log(AttributeTargetTypes = "*", 
 EntryLevel = LogLevel.Debug, ExitLevel = LogLevel.Debug, 
 ExceptionLevel = LogLevel.Error)]

0

ฉันไม่แน่ใจว่าเฟรมเวิร์กใดทำสิ่งนี้หรือไม่ แต่จากมุมมองของการออกแบบฉันจะสร้างแบบจำลองข้อมูลที่ต้องล็อกอินเป็นสามประเภทหลัก ๆ :

  1. การติดตามระดับเมธอด
  2. การบันทึกข้อยกเว้น
  3. นักพัฒนาข้อมูลรันไทม์พิเศษเชื่อว่าเป็นสิ่งสำคัญสำหรับการตรวจสอบในกรณีที่เกิดความล้มเหลวในการทำงาน (หรือพฤติกรรมใด ๆ ที่เกี่ยวข้องกับสถานการณ์เฉพาะรันไทม์)

สำหรับสองประเภทแรกกรอบการบันทึกที่ดีที่สุดของฉันควรจัดการพวกเขาเป็นกระบวนการสร้างโพสต์และโปร่งใสสำหรับนักพัฒนา มันเป็นการดีที่จะประกาศเพิ่มการบันทึกลงในแอสเซมบลีอย่างเช่น:

Trace YourNamespace.* [public methods|constructors]
{  # options
   ignore trivial methods,
   format: "{time stamp}: {method name}({parameter list})",
   condition: "{Context.Instance.UserID in (12432,23432)}",
}

Exception YourNamespace.Program.Main [unhandled exceptions]
{
  format: "{time stamp}: {Context.Instance.UserId} {exception}",
  action: die,  # options are throw, swallow,
}

สำหรับหมวดหมู่ที่ 3 โปรแกรมเมอร์สามารถสร้างวิธีการ "บันทึกข้อมูล" หนึ่งวิธีขึ้นไปและใช้ประโยชน์จากการสืบค้นกลับสำหรับหมวดหมู่แรก วิธีการบันทึกไม่ได้ทำอะไรมากไปกว่าการให้บริการจุดสตับที่สามารถใช้กฎการติดตามได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.