• C#开发高性能Log Help 类设计开发


    概述

    项目中要在操作数据库的异常处理中加入写Log日志,对于商业上有要求,写log时对其它操作尽可能影响小,不能因为加入log导致耗时太多.

    设计思想

    在写入日志时利用Queue来管理,写日志有一个专门的backgroud线程来处理,如果没有日志要写,这个线程处于wait状态,这就有了线程的异步处理.

    简单的实现方式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    //<summary>
    //Write Log
    //<summary>
    public static void WriteLog(string logFile, string msg)
    {
        try
        {
            System.IO.StreamWriter sw = System.IO.File.AppendText(
                    logPath + LogFilePrefix +" "+ logFile + " " +
                    DateTime.Now.ToString("yyyyMMdd") + ".Log"
                );
            sw.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:  ") + msg);
            sw.Close();
        }
        catch (Exception)
        {
             
            throw;
        }
    }

    我们的设计图

    image

    而后我们在AddLogMessage时semaphore.Release()就能唤醒wait中的log 线程.

    代码设计

    /// <summary> 
    /// Author: spring yang 
    /// Create time:2012/3/30 
    /// Log Help class 
    /// </summary> 
    /// <remarks>High performance log class</remarks> 
    public class Log : IDisposable 

        //Log Message queue 
        private static Queue<LogMessage> _logMessages;

        //log save directory 
        private static string _logDirectory;

        //log write file state 
        private static bool _state;

        //log type 
        private static LogType _logType;

        //log life time sign 
        private static DateTime _timeSign;

        //log file stream writer 
        private static StreamWriter _writer;

        /// <summary> 
        /// Wait enqueue wirte log message semaphore will release 
        /// </summary> 
        private Semaphore _semaphore;

        /// <summary> 
        /// Single instance 
        /// </summary> 
        private static Log _log;

        /// <summary> 
        /// Gets a single instance 
        /// </summary> 
        public static Log LogInstance 
        { 
            get { return _log ?? (_log = new Log()); } 
        }

        private object _lockObjeck;

        /// <summary> 
        /// Initialize Log instance 
        /// </summary> 
        private void Initialize() 
        { 
            if (_logMessages == null) 
            {   _state = true; 
                string logPath = System.Configuration.ConfigurationManager.AppSettings["LogDirectory"]; 
                _logDirectory = string.IsNullOrEmpty(logPath) ? ".\\log\\" : logPath; 
                if (!Directory.Exists(_logDirectory)) Directory.CreateDirectory(_logDirectory); 
                _logType = LogType.Daily; 
                _lockObjeck=new object(); 
                _semaphore = new Semaphore(0, int.MaxValue, Constants.LogSemaphoreName); 
                _logMessages = new Queue<LogMessage>(); 
                var thread = new Thread(Work) {IsBackground = true}; 
                thread.Start(); 
            } 
        }

        /// <summary> 
        /// Create a log instance 
        /// </summary> 
        private Log() 
        { 
            Initialize(); 
        }

        /// <summary> 
        /// Log save name type,default is daily 
        /// </summary> 
        public LogType LogType 
        { 
            get { return _logType; } 
            set { _logType = value; } 
        }

        /// <summary> 
        /// Write Log file  work method 
        /// </summary> 
        private void Work() 
        { 
            while (true) 
            { 
                //Determine log queue have record need wirte 
                if (_logMessages.Count > 0) 
                { 
                    FileWriteMessage(); 
                } 
                else 
                    if (WaitLogMessage()) break; 
            } 
        }

        /// <summary> 
        /// Write message to log file 
        /// </summary> 
        private void FileWriteMessage() 
        { 
            LogMessage logMessage=null; 
            lock (_lockObjeck) 
            { 
                if(_logMessages.Count>0) 
                logMessage = _logMessages.Dequeue(); 
            } 
            if (logMessage != null) 
            { 
                FileWrite(logMessage); 
            } 
        }

        /// <summary> 
        /// The thread wait a log message 
        /// </summary> 
        /// <returns>is close or not</returns> 
        private bool WaitLogMessage() 
        { 
            //determine log life time is true or false 
            if (_state) 
            { 
                WaitHandle.WaitAny(new WaitHandle[] { _semaphore }, -1, false); 
                return false; 
            } 
            FileClose(); 
            return true; 
        }

        /// <summary> 
        /// Gets file name by log type 
        /// </summary> 
        /// <returns>log file name</returns> 
        private string GetFilename() 
        { 
            DateTime now = DateTime.Now; 
            string format = ""; 
            switch (_logType) 
            { 
                case LogType.Daily: 
                    _timeSign = new DateTime(now.Year, now.Month, now.Day); 
                    _timeSign = _timeSign.AddDays(1); 
                    format = "yyyyMMdd'.log'"; 
                    break; 
                case LogType.Weekly: 
                    _timeSign = new DateTime(now.Year, now.Month, now.Day); 
                    _timeSign = _timeSign.AddDays(7); 
                    format = "yyyyMMdd'.log'"; 
                    break; 
                case LogType.Monthly: 
                    _timeSign = new DateTime(now.Year, now.Month, 1); 
                    _timeSign = _timeSign.AddMonths(1); 
                    format = "yyyyMM'.log'"; 
                    break; 
                case LogType.Annually: 
                    _timeSign = new DateTime(now.Year, 1, 1); 
                    _timeSign = _timeSign.AddYears(1); 
                    format = "yyyy'.log'"; 
                    break; 
            } 
            return now.ToString(format); 
        }

        /// <summary> 
        /// Write log file message 
        /// </summary> 
        /// <param name="msg"></param> 
        private void FileWrite(LogMessage msg) 
        { 
            try 
            { 
                if (_writer == null) 
                { 
                    FileOpen(); 
                } 
                else 
                { 
                    //determine the log file is time sign 
                    if (DateTime.Now >= _timeSign) 
                    { 
                        FileClose(); 
                        FileOpen(); 
                    } 
                    _writer.WriteLine(Constants.LogMessageTime+msg.Datetime); 
                    _writer.WriteLine(Constants.LogMessageType+msg.Type); 
                    _writer.WriteLine(Constants.LogMessageContent+msg.Text); 
                    _writer.Flush(); 
                } 
            } 
            catch (Exception e) 
            { 
                Console.Out.Write(e); 
            } 
        }

        /// <summary> 
        /// Open log file write log message 
        /// </summary> 
        private void FileOpen() 
        { 
            _writer = new StreamWriter(Path.Combine(_logDirectory, GetFilename()), true, Encoding.UTF8); 
        }

        /// <summary> 
        /// Close log file 
        /// </summary> 
        private void FileClose() 
        { 
            if (_writer != null) 
            { 
                _writer.Flush(); 
                _writer.Close(); 
                _writer.Dispose(); 
                _writer = null; 
            } 
        }

        /// <summary> 
        /// Enqueue a new log message and release a semaphore 
        /// </summary> 
        /// <param name="msg">Log message</param> 
        public void Write(LogMessage msg) 
        { 
            if (msg != null) 
            { 
                lock (_lockObjeck) 
                { 
                    _logMessages.Enqueue(msg); 
                    _semaphore.Release(); 
                } 
            } 
        }

        /// <summary> 
        /// Write message by message content and type 
        /// </summary> 
        /// <param name="text">log message</param> 
        /// <param name="type">message type</param> 
        public void Write(string text, MessageType type) 
        { 
            Write(new LogMessage(text, type)); 
        }

        /// <summary> 
        /// Write Message by datetime and message content and type 
        /// </summary> 
        /// <param name="dateTime">datetime</param> 
        /// <param name="text">message content</param> 
        /// <param name="type">message type</param> 
        public void Write(DateTime dateTime, string text, MessageType type) 
        { 
            Write(new LogMessage(dateTime, text, type)); 
        }

        /// <summary> 
        /// Write message ty exception and message type 
        /// </summary> 
        /// <param name="e">exception</param> 
        /// <param name="type">message type</param> 
        public void Write(Exception e, MessageType type) 
        { 
            Write(new LogMessage(e.Message, type)); 
        }

        #region IDisposable member

        /// <summary> 
        /// Dispose log 
        /// </summary> 
        public void Dispose() 
        { 
            _state = false; 
        }

        #endregion 
    }

    /// <summary> 
    /// Log Type 
    /// </summary> 
    /// <remarks>Create log by daily or weekly or monthly or annually</remarks> 
    public enum LogType 

        /// <summary> 
        /// Create log by daily 
        /// </summary> 
        Daily,

        /// <summary> 
        /// Create log by weekly 
        /// </summary> 
        Weekly,

        /// <summary> 
        /// Create log by monthly 
        /// </summary> 
        Monthly,

        /// <summary> 
        /// Create log by annually 
        /// </summary> 
        Annually 
    }

    /// <summary> 
    /// Log Message Class 
    /// </summary> 
    public class LogMessage 
    {

        /// <summary> 
        /// Create Log message instance 
        /// </summary> 
        public LogMessage() 
            : this("", MessageType.Unknown) 
        { 
        }

        /// <summary> 
        /// Crete log message by message content and message type 
        /// </summary> 
        /// <param name="text">message content</param> 
        /// <param name="messageType">message type</param> 
        public LogMessage(string text, MessageType messageType) 
            : this(DateTime.Now, text, messageType) 
        { 
        }

        /// <summary> 
        /// Create log message by datetime and message content and message type 
        /// </summary> 
        /// <param name="dateTime">date time </param> 
        /// <param name="text">message content</param> 
        /// <param name="messageType">message type</param> 
        public LogMessage(DateTime dateTime, string text, MessageType messageType) 
        { 
            Datetime = dateTime; 
            Type = messageType; 
            Text = text; 
        }

        /// <summary> 
        /// Gets or sets datetime 
        /// </summary> 
        public DateTime Datetime { get; set; }

        /// <summary> 
        /// Gets or sets message content 
        /// </summary> 
        public string Text { get; set; }

        /// <summary> 
        /// Gets or sets message type 
        /// </summary> 
        public MessageType Type { get; set; }

        /// <summary> 
        /// Get Message to string 
        /// </summary> 
        /// <returns></returns> 
        public new string ToString() 
        { 
            return Datetime.ToString(CultureInfo.InvariantCulture) + "\t" + Text + "\n"; 
        } 
    }

    /// <summary> 
    /// Log Message Type enum 
    /// </summary> 
    public enum MessageType 

        /// <summary> 
        /// unknown type 
        /// </summary> 
        Unknown,

        /// <summary> 
        /// information type 
        /// </summary> 
        Information,

        /// <summary> 
        /// warning type 
        /// </summary> 
        Warning,

        /// <summary> 
        /// error type 
        /// </summary> 
        Error,

        /// <summary> 
        /// success type 
        /// </summary> 
        Success 
    }

    Test Case:

    1
    2
    3
    4
    5
    6
    7
    public static void TestLog()
    {
        Log.LogInstance.Write(  "Test Message",MessageType.Information);
        Log.LogInstance.Write("one",MessageType.Error);
        Log.LogInstance.Write("two", MessageType.Success);
        Log.LogInstance.Write("three", MessageType.Warning);
    }

    运行结果:

    image

    接受Mainz的建议,改了部分代码,

    Mainz:http://www.cnblogs.com/Mainz/

    spring yang

    Making Time for the MITs (Most Important Things)

    DICOM Query 和Retrieve 的方法和定义

     

    概要

    在用DCMTK库来写query和retrieve的时候查询条件的定义不是很清楚,DICOM的query和数据库的select 有什么区别呢?

    Query即C-Find, Retrieve 即C-Move, 在放射影像设备、系统中是仅次于C-store(发送影像)应用最多的命令, 相较c-sore, c-find/c-move 要复杂很.....

    Query/retrieve 简化序列图 (只有一个c-move-rsp 回合)

    对于SCU (发起方)

    通俗来讲Query 是指请求对方系统把 请求某个级别(Patient/Study/Series/Image)的信息传送给自己。当然也可以进行关联查询要求对方把各个级别的信息都返回给自己。

    Retrieve是指SCU通过Query 拿到信息后,要求对方根据请求级别 (Patient/Study/Series/Image) 发送影像给己方。

    对于SCP (服务提供方)

    需要提供各个级别的服务供SCU Query/Retrieve.

    C-Find/C-Move 有几个重要的概念

    一个是 模型(Information model),

    Patient root

    Study root

    Patientstudy root

    一个是 级别(Query/retrieve level)

    Patient level, study level, series level, image level

    Patient root 下可有包含patient Level 在内的四个level

    Study root 下只有三个level, study root 下不能q/r patient level 信息与影像。

    C-find

    既然需要依据某个条件查询一组或某个patient/study/series/image, 必须需要一个或一组查询键值, 同时需要告诉对方应该返回什么, 还有怎样区分每组数据。DICOM 中定义的query key, return key and unique key.

    Unique key 为 patient id, study instance UID, series instance UID, SOP instance UID.

    Query key 对应每个level 有不同的值,常见的比如检查级的studydate, 序列级的modality等,具体参见DICOM和IHE.

    Unique key 必须在返回列中,否则SCU无法区分各组数据。

    有些Q/R SCP 支持关联查询,SCU 可以根据Study level的query key来获取series.

    C-move 相对简单

    SCU 发送请求前,启动一个storage SCP 用于接收影像,

    依据c-find 的返回结果,读取unique key,

    Patient root

    Patient level Patient id

    Study level patient id, study instance UID

    Series level patient id, study instance UID, series instance UID

    Image level patient id, study instance UID, series instance UID, SOP instance UID

    Study root

    Study level study instance UID

    Series level study instance UID, series instance UID

    Image level study instance UID, series instance UID, SOP instance UID

    每个level, 自己及上一个级别的unique key 必须提供作为query key.

    C-move 中还有一个概念叫Retrieve Destination AE. 通俗讲,就是告诉对方应该发送影像到什么地方。对于SCU, 通常它可以让他发送给自己,也可以让它发送给另一个AE(甚至另一台机器,系统)。这一点也是C-move 和C-Get的区别所在,C-get 只能要求对方发给自己。

    Q/R SCU告诉对方(Q/R SCP) unique key 和retrieve destination, SCP通过解析retrieve destination AE title 获取在SCP方注册的IP和端口,发送影像至此storage SCP, 同时发送c-move-rsp 到 Q/R SCU. 当影像发送结束后会发送实际统计结果至Q/R SCU.

    我们来一个简单有查询定义:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    // Fill the query object
           DCXOBJ obj = new DCXOBJ();
           DCXELM  el = new DCXELM();
     
           el.Init((int)DICOM_TAGS_ENUM.QueryRetrieveLevel);
           el.Value = "PATIENT";
           obj.insertElement(el);
     
           el.Init(0x00100010);
           el.Value = "R*";
           obj.insertElement(el);
     
           el.Init(0x00100020);
           obj.insertElement(el);
     
           el.Init((int)DICOM_TAGS_ENUM.PatientsSex);
           obj.insertElement(el);
     
           el.Init((int)DICOM_TAGS_ENUM.PatientsBirthDate);
           obj.insertElement(el);

    相对应的SQL语句就是:

    1
    2
    3
    SELECT [PATIENT NAME] , [PATIENT ID], [PATIENT SEX], [PATIENT BIRTH DATA]
    FROM PATIENT
    WHERE [PATIENT NAME] like "R%"

    下面是Study层次的实例图

    // Fill the query object

    DCXOBJ obj = new DCXOBJ();

    DCXELM el = new DCXELM();

     

                el.Init((int)DICOM_TAGS_ENUM.studyInstanceUID);

                obj.insertElement(el);

    SELECT

    [STUDY INSTANCE UID],

    el.Init((int)DICOM_TAGS_ENUM.StudyDate);

                obj.insertElement(el);

    [STUDY DATE],

    el.Init((int)DICOM_TAGS_ENUM.StudyDescription);

                obj.insertElement(el);

    [STUDY DESCRIPTION],

    el.Init((int)DICOM_TAGS_ENUM.ModalitiesInStudy);

                obj.insertElement(el);

    [MODALITIES IN STUDY]

         el.Init((int)DICOM_TAGS_ENUM.QueryRetrieveLevel);

                el.Value = "STUDY";

                obj.insertElement(el);

    FROM STUDY

                el.Init((int)DICOM_TAGS_ENUM.patientName);

                el.Value = "REIMOND^GOLDA";

                obj.insertElement(el);

    WHERE

    [PATIENT NAME] = ‘REIMOND^GOLDA’

    el.Init((int)DICOM_TAGS_ENUM.patientID);

                el.Value = "123456789";

                obj.insertElement(el);

    AND

    [PATIENT ID] = ‘123456789’

    以上例子就相当于是

    1
    2
    3
    SELECT [PATIENT NAME] , [PATIENT ID], [STUDY INSTANCE UID], [STUDY DATE], [STUDY DESCRIPTION], [MODALITIES IN STUDY]
    FROM STUDY
    WHERE [PATIENT NAME] = ‘REIMOND^GOLDA’ AND [PATIENT ID] = ‘123456789’

    以下是where时候的参照表

    符号

    意思

    相当于SQL 的意思

    *

    零个或更多的字符,简单的说n个字符.

    WHERE PATIENT NAME LIKE “COHEN%”

    ?

    一个字符

    “COH?N” 可以符合 “COHEN” 和 “COHAN” 还有这个 “COH N”

    我不建议使用这个

    -

    对于时间和日期来的描述

    FROM – TO 以下格式

    YYYYMMDD-YYYYMMDD

    WHERE STUDY DATE BETWEEN 19950101 AND 20110911

    YYYYMMDD-

    WHERE STUDY DATE >= 19950101

    -YYYYMMDD

    WHERE STUDY DATE <= 20110911

    \

    符合条件的值

    LOCALIZER\AXIAL

    WHERE IMAGE TYPE in (‘LOCALIZER’, ‘AXIAL’)

    以下这张图展示了query的level

    1

    以下是一个模型来验证query的level

    UID

    Name

    Query Levels

    Comment

    1.2.840.10008.5.1.4.1.2.1.1

    Patient Root Query/Retrieve Information Model - FIND

    PATIENT

    STUDY

    SERIES

    IMAGE

    Use it!

    1.2.840.10008.5.1.4.1.2.1.2

    Patient Root Query/Retrieve Information Model - MOVE

    PATIENT

    STUDY

    SERIES

    IMAGE

    Use it!

    1.2.840.10008.5.1.4.1.2.2.1

    Study Root Query/Retrieve Information Model - FIND

    STUDY

    SERIES

    IMAGE

    Use it if Patient root doesn’t work for you

    1.2.840.10008.5.1.4.1.2.2.2

    Study Root Query/Retrieve Information Model - MOVE

    STUDY

    SERIES

    IMAGE

    Use it if Patient root doesn’t work for you

    1.2.840.10008.5.1.4.1.2.3.1

    Patient/Study Only Query/Retrieve Information Model - FIND (Retired)

    PATIENT

    STUDY

    Don’t use

    1.2.840.10008.5.1.4.1.2.3.2

    Patient/Study Only Query/Retrieve Information Model - MOVE (Retired)

    PATIENT

    STUDY

    Don’t use

    参考资料

    1、http://dicomiseasy.blogspot.com/2012/01/dicom-queryretrieve-part-i.html

    2、http://blog.csdn.net/lucky2all/article/details/3307753

    欢迎各位参与讨论,如果觉得对你有帮助,请点击image    推荐下,万分谢谢.

    作者:spring yang

    出处:http://www.cnblogs.com/springyangwc/

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

  • 相关阅读:
    Backbone学习之todos实现
    由$(this).attr('id')引出的一点感想
    JS类的实现
    JS设计模式之接口
    JS设计模式之工厂模式
    JS设计模式之序
    JS的继承
    you think you know javascript?
    js中的null和undefined
    作用域、闭包等概念的理解
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2426862.html
Copyright © 2020-2023  润新知