使用MONO在MAC OS上开发——同步日志(一)
原先打算再写点EXT.NET方面的,由于现在在玩Silverlight和Android,所以不打算写下去了。而且最近很忙,所以发帖也不会很及时。
好了,转入正题了:最近客户有个需求,要求写个小程序监控生产线上的苹果一体机的日志文件并上传到服务器。开始想使用Perl或者Python,但是时间不够(因为对这两门语言还不太熟悉),于是想试试MONO。MONO虽然没想象中好用,但是还算勉强能用。
虽然MonoDevelop还可以,但是还是比较喜欢使用VS开发。于是在VS里面先编码。值得注意的是:
- 如果是开发Winform程序,界面用GTK重画吧。
- 如果是在VS里面开发,最好将所有的编码和注释都用英文。否则换其他系统时,可能会出现乱码。
- 编写时需注意MONO是否支持,否则就是白写。
接下来,开始编码。
1.读取配置文件
习惯用XML,发现不支持System.Xml.Linq,亦不支持System.Xml,读取ini文件也麻烦,于是干脆读取文本文件好了。示例配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | MonitoringDirectoryType:['Immediate','Cycle'] Path:['/Users/PRODUCTION/Public/FinalTest/LogsFolder','/Users/PRODUCTION/Public/FinalTest/LogsFolder'] TargetPath:['/Volumes/mes_data/FINALTEST/n81a/{Y}/{M}/{D}/FIN012','/Volumes/mes_data/FINALTEST/n81a/{Y}/{M}/{D}/FIN012'] IncludeSubdirectories:['FALSE','false'] Filter:['*.TXT','*.CSV'] BackupPath:['/Users/PRODUCTION/BACKUP/{Y}/{M}/{D}','/Users/PRODUCTION/BACKUP/{Y}/{M}/{D}'] BackupExpired:['2','2'] CycleMinutes:['','1440'] |
相关解析代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using MonitoringApp.Custom; using System.Text.RegularExpressions; using Gtk; namespace MonitoringApp.Code.App { public class Configuration { private static bool isLoadFirst = false ; private static List<MyConfig> configLst; public static List<MyConfig> ConfigLst { get { if (configLst == null ) { try { if (!isLoadFirst) return LoadFromInitParams(); else return null ; } catch (Exception ex) { LogManager.WriteErrorLog(ex); return null ; } } return configLst; } set { configLst = value; } } /// <summary> /// Load Configuration file /// </summary> /// <returns></returns> private static List<MyConfig> LoadFromInitParams() { isLoadFirst = true ; string _configPath = Path.Combine(Directory.GetCurrentDirectory(), "Configuration.txt" ); if (File.Exists(_configPath)) { if (ConfigLst == null ) ConfigLst = new List<MyConfig>(); else return ConfigLst; using (StreamReader sr = new StreamReader(_configPath)) { int lineIndex = 1; while (sr.Peek() > 0) { string str = sr.ReadLine().Trim(); if (str.Length == 0) continue ; if (!str.Contains( "[" ) || !str.Contains( "]" )) { LogManager.WriteErrorLog( string .Format( "Config Error:'[' OR ']' Not Exist.At Line {0}." , lineIndex)); continue ; } if (!str.Contains( ":" )) { LogManager.WriteErrorLog( string .Format( "Config Error:':' Not Exist.At Line {0}." , lineIndex)); continue ; } string [] names = str.Split( ':' ); str = str.Substring(str.IndexOf( ':' ) + 1).Trim( ']' ).Trim( '[' ).Trim( '\'' ); string [] strConfigs = str.Split( ',' ); SetConfig(names, ConfigLst, strConfigs, lineIndex == 1); lineIndex++; } } return ConfigLst; } else { LogManager.WriteErrorLog( string .Format( "can't find config file.Path:{0}." ,_configPath)); return null ; } } /// <summary> /// Set Config /// </summary> /// <param name="name"></param> /// <param name="lst"></param> /// <param name="strConfigs"></param> private static void SetConfig( string [] name, List<MyConfig> lst, string [] strConfigs, bool isLineOne) { try { var mcConfig = (MonitoringConfigurations)Enum.Parse( typeof (MonitoringConfigurations), name[0], true ); //Set Values for ( int i = 0; i < strConfigs.Length; i++) { string value = strConfigs[i].Trim( '\'' ); switch (mcConfig) { case MonitoringConfigurations.MonitoringDirectoryType: { #region MonitoringDirectoryType Defalut:immediate switch (value.ToLower()) { case "immediate" : { if (isLineOne) { MyConfig myConfig = new MyConfig(); myConfig.MDType = MonitoringDirectoryType.Immediate; lst.Add(myConfig); } else { lst[i].MDType = MonitoringDirectoryType.Immediate; } break ; } case "cycle" : { if (isLineOne) { MyConfig myConfig = new MyConfig(); myConfig.MDType = MonitoringDirectoryType.Cycle; lst.Add(myConfig); } else { lst[i].MDType = MonitoringDirectoryType.Cycle; } break ; } default : { if (isLineOne) { MyConfig myConfig = new MyConfig(); myConfig.MDType = MonitoringDirectoryType.Immediate; lst.Add(myConfig); } else { lst[i].MDType = MonitoringDirectoryType.Immediate; } break ; } } #endregion break ; } case MonitoringConfigurations.Path: #region Path if (isLineOne) { MyConfig myConfig = new MyConfig(); myConfig.Path = value; lst.Add(myConfig); } else { lst[i].Path = value; } #endregion break ; case MonitoringConfigurations.IncludeSubdirectories: #region IncludeSubdirectories if (isLineOne) { MyConfig myConfig = new MyConfig(); myConfig.IncludeSubdirectories = Convert.ToBoolean(value); lst.Add(myConfig); } else { lst[i].IncludeSubdirectories = Convert.ToBoolean(value); } #endregion break ; case MonitoringConfigurations.Filter: #region Filter if (isLineOne) { MyConfig myConfig = new MyConfig(); myConfig.Filter = value; lst.Add(myConfig); } else { lst[i].Filter = value; } break ; #endregion case MonitoringConfigurations.BackupPath: #region BackupPath if (isLineOne) { MyConfig myConfig = new MyConfig(); myConfig.BackupPath = value; lst.Add(myConfig); } else { lst[i].BackupPath = value; } #endregion break ; case MonitoringConfigurations.TargetPath: #region TargetPath if (isLineOne) { MyConfig myConfig = new MyConfig(); myConfig.TargetPath = value; lst.Add(myConfig); } else { lst[i].TargetPath = value; } #endregion break ; case MonitoringConfigurations.BackupExpired: #region BackupExpired Unit:days Default:30 if (isLineOne) { MyConfig myConfig = new MyConfig(); myConfig.BackupExpired = Convert.ToInt32(value.Length == 0 ? "30" : value); lst.Add(myConfig); } else { lst[i].BackupExpired = Convert.ToInt32(value.Length == 0 ? "30" : value); } #endregion break ; case MonitoringConfigurations.CycleMinutes: #region CycleMinutes Unit:Minute Default:60 if (isLineOne) { MyConfig myConfig = new MyConfig(); myConfig.CycleMinutes = Convert.ToInt32(value.Length == 0 ? "60" : value); lst.Add(myConfig); } else { lst[i].CycleMinutes = Convert.ToInt32(value.Length == 0 ? "60" : value); } #endregion break ; default : break ; } } } catch (Exception ex) { LogManager.WriteErrorLog(ex); } } } } |
值得注意的是:
- 这里是按行读取的。从读取配置来看,可以看出,是支持多个配置项的。
- 如果出错,会记录日志。
- 和客户讲解如何配置是一件麻烦的事情。相关配置解释如下:
1 2 3 4 5 6 7 8 | MonitoringDirectoryType:目录类型,仅支持两个值(Immediate、Cycle)。Immediate表示即时监控(默认值),Cycle表示周期监控。 Path:监控目录路径。必须是存在的路径。 TargetPath:目标目录路径。可以是远程目录路径。不能使用SMB路径,而应该是用如“/Volumes/mes_data/n81a”的类型。 IncludeSubdirectories:是否涵盖子目录。 Filter:过滤字符串。如“*”表示监控所有文件,“*.txt”表示监控所有的文本文件。 BackupPath:备份路径。 BackupExpired:备份过期时间。单位是天。必须为整数。默认值30天。 CycleMinutes:周期循环时间。单位是分。 必须为整数。默认值60分钟。 |
然后针对配置写个说明文档,这样客户就会配置了。
2)记录日志
程序运行起来总不可避免的会遇到各种问题,记录日志就不可避免了。相关代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using Gtk; namespace MonitoringApp.Code.App { public class LogManager { public static object obj = new object(); static LogManager() { } #region Fields and Properties private static string _logPath = string.Empty; public static string LogPath { get { lock (obj) { if (_logPath == string.Empty) { var _logStr = string.Format("{0}/log/{1}/{2}", Path.Combine(Directory.GetCurrentDirectory(), "Data"), DateTime.Now.ToString("yyyy-MM"), DateTime.Now.Day); if (!System.IO.Directory.Exists(_logStr)) System.IO.Directory.CreateDirectory(_logStr); return string.Format("{0}/", _logStr); } return _logPath; } } set { _logPath = value; } } private static string _logFielPrefix = string.Empty; public static string LogFielPrefix { get { return _logFielPrefix; } set { _logFielPrefix = value; } } #endregion #region Log public static void WriteLog(string logFile, string msg) { try { lock (obj) { var _sb = new System.Text.StringBuilder(); _sb.Append(LogPath).Append(LogFielPrefix).Append(logFile).Append(".Log"); using (var _sw = System.IO.File.AppendText(_sb.ToString())) { _sw.WriteLine("==============================={0}===============================", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); _sw.WriteLine(msg); _sw.Flush(); _sw.Close(); Console.WriteLine(msg); } } } catch { } } public static void WriteLog(LogFile logFile, string msg) { WriteLog(logFile.ToString(), msg); } public static void WriteTraceLog(string msg) { Console.WriteLine(msg); } public static void WriteErrorLog(string msg) { WriteLog(LogFile.Error, msg); } public static void WriteErrorLog(Exception ex) { var _sbError = new StringBuilder(); _sbError.Append("[Message]").Append(ex.Message); _sbError.Append("\r\n[TargetSite]").Append(ex.TargetSite); _sbError.Append("\r\n[StackTrace]").Append(ex.StackTrace); _sbError.Append("\r\n[Source]").Append(ex.Source); foreach (var _item in ex.Data.Keys) { _sbError.Append("\r\n[").Append(_item).Append("]").Append(ex.Data[_item]); } if (ex.InnerException != null && ex.InnerException.Message != ex.Message) { _sbError.AppendLine("____________________________________________________________________________________"); _sbError.Append("[Message]").Append(ex.InnerException.Message); _sbError.Append("\r\n[TargetSite]").Append(ex.InnerException.TargetSite); _sbError.Append("\r\n[StackTrace]").Append(ex.InnerException.StackTrace); _sbError.Append("\r\n[Source]").Append(ex.InnerException.Source); _sbError.AppendLine("____________________________________________________________________________________"); } WriteLog(LogFile.Error, _sbError.ToString()); } public static void WriteWarningLog(string msg) { WriteLog(LogFile.Warning, msg); } #endregion } public enum LogFile { Trace, Warning, Error, SQL, System } } |
值得注意的是“Directory.GetCurrentDirectory()”可以获取到当前用户的个人目录,至于获取到程序当前目录路径,Winform的那一套不管用(如果哪位朋友找到了适合的方法,请留言)。苹果系统和Window系统差别太大,从没玩过苹果系统,刚开始玩还挺不习惯的。
接下来,讲述如何监控目录以及定时备份,最后讲述如何发布部署。发布部署才是最重要的一环。不上生产线,就不知道会有什么问题。当初以为这个小程序,一天就能搞定,结果中秋3天都废了。还好万幸搞定。时间关系,就此打住。
出处:http://www.cnblogs.com/codelove/
沟通渠道:编程交流群<85318032> 产品交流群<897857351>
如果喜欢作者的文章,请关注【麦扣聊技术】订阅号以便第一时间获得最新内容。本文版权归作者和湖南心莱信息科技有限公司共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
静听鸟语花香,漫赏云卷云舒。

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架