├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── myslowparser.iml └── src ├── main.rs ├── processing.rs ├── regs.rs ├── types.rs └── web.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | /.idea 4 | *.log 5 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "aho-corasick" 3 | version = "0.6.8" 4 | source = "registry+https://github.com/rust-lang/crates.io-index" 5 | dependencies = [ 6 | "memchr 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 7 | ] 8 | 9 | [[package]] 10 | name = "ansi_term" 11 | version = "0.11.0" 12 | source = "registry+https://github.com/rust-lang/crates.io-index" 13 | dependencies = [ 14 | "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", 15 | ] 16 | 17 | [[package]] 18 | name = "arrayvec" 19 | version = "0.4.7" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | dependencies = [ 22 | "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", 23 | ] 24 | 25 | [[package]] 26 | name = "atty" 27 | version = "0.2.11" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | dependencies = [ 30 | "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", 31 | "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", 32 | "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", 33 | ] 34 | 35 | [[package]] 36 | name = "base64" 37 | version = "0.6.0" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | dependencies = [ 40 | "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 41 | "safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 42 | ] 43 | 44 | [[package]] 45 | name = "base64" 46 | version = "0.9.2" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | dependencies = [ 49 | "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 50 | "safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 51 | ] 52 | 53 | [[package]] 54 | name = "bitflags" 55 | version = "1.0.3" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | 58 | [[package]] 59 | name = "byteorder" 60 | version = "1.2.6" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | 63 | [[package]] 64 | name = "cfg-if" 65 | version = "0.1.5" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | 68 | [[package]] 69 | name = "chrono" 70 | version = "0.4.6" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | dependencies = [ 73 | "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", 74 | "num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", 75 | "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", 76 | ] 77 | 78 | [[package]] 79 | name = "clap" 80 | version = "2.32.0" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | dependencies = [ 83 | "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", 84 | "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", 85 | "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 86 | "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 87 | "textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", 88 | "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 89 | "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", 90 | ] 91 | 92 | [[package]] 93 | name = "cookie" 94 | version = "0.9.2" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | dependencies = [ 97 | "base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", 98 | "ring 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", 99 | "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", 100 | "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", 101 | ] 102 | 103 | [[package]] 104 | name = "crossbeam-deque" 105 | version = "0.2.0" 106 | source = "registry+https://github.com/rust-lang/crates.io-index" 107 | dependencies = [ 108 | "crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 109 | "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 110 | ] 111 | 112 | [[package]] 113 | name = "crossbeam-epoch" 114 | version = "0.3.1" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | dependencies = [ 117 | "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", 118 | "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 119 | "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 120 | "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 121 | "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 122 | "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", 123 | "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 124 | ] 125 | 126 | [[package]] 127 | name = "crossbeam-utils" 128 | version = "0.2.2" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | dependencies = [ 131 | "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 132 | ] 133 | 134 | [[package]] 135 | name = "gcc" 136 | version = "0.3.54" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | 139 | [[package]] 140 | name = "httparse" 141 | version = "1.3.2" 142 | source = "registry+https://github.com/rust-lang/crates.io-index" 143 | 144 | [[package]] 145 | name = "hyper" 146 | version = "0.10.13" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | dependencies = [ 149 | "base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", 150 | "httparse 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", 151 | "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 152 | "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 153 | "mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 154 | "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 155 | "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", 156 | "traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 157 | "typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 158 | "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 159 | "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", 160 | ] 161 | 162 | [[package]] 163 | name = "idna" 164 | version = "0.1.5" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | dependencies = [ 167 | "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 168 | "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 169 | "unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 170 | ] 171 | 172 | [[package]] 173 | name = "isatty" 174 | version = "0.1.8" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | dependencies = [ 177 | "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", 178 | "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", 179 | "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", 180 | ] 181 | 182 | [[package]] 183 | name = "language-tags" 184 | version = "0.2.2" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | 187 | [[package]] 188 | name = "lazy_static" 189 | version = "0.2.11" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | 192 | [[package]] 193 | name = "lazy_static" 194 | version = "1.1.0" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | dependencies = [ 197 | "version_check 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 198 | ] 199 | 200 | [[package]] 201 | name = "libc" 202 | version = "0.2.43" 203 | source = "registry+https://github.com/rust-lang/crates.io-index" 204 | 205 | [[package]] 206 | name = "log" 207 | version = "0.3.9" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | dependencies = [ 210 | "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", 211 | ] 212 | 213 | [[package]] 214 | name = "log" 215 | version = "0.4.4" 216 | source = "registry+https://github.com/rust-lang/crates.io-index" 217 | dependencies = [ 218 | "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 219 | ] 220 | 221 | [[package]] 222 | name = "matches" 223 | version = "0.1.8" 224 | source = "registry+https://github.com/rust-lang/crates.io-index" 225 | 226 | [[package]] 227 | name = "memchr" 228 | version = "2.0.2" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | dependencies = [ 231 | "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", 232 | ] 233 | 234 | [[package]] 235 | name = "memoffset" 236 | version = "0.2.1" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | 239 | [[package]] 240 | name = "mime" 241 | version = "0.2.6" 242 | source = "registry+https://github.com/rust-lang/crates.io-index" 243 | dependencies = [ 244 | "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 245 | ] 246 | 247 | [[package]] 248 | name = "myslowparser" 249 | version = "1.1.7" 250 | dependencies = [ 251 | "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 252 | "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", 253 | "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 254 | "regex 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 255 | "rocket 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", 256 | "rocket_codegen 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", 257 | ] 258 | 259 | [[package]] 260 | name = "nodrop" 261 | version = "0.1.12" 262 | source = "registry+https://github.com/rust-lang/crates.io-index" 263 | 264 | [[package]] 265 | name = "num-integer" 266 | version = "0.1.39" 267 | source = "registry+https://github.com/rust-lang/crates.io-index" 268 | dependencies = [ 269 | "num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", 270 | ] 271 | 272 | [[package]] 273 | name = "num-traits" 274 | version = "0.2.5" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | 277 | [[package]] 278 | name = "num_cpus" 279 | version = "1.8.0" 280 | source = "registry+https://github.com/rust-lang/crates.io-index" 281 | dependencies = [ 282 | "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", 283 | ] 284 | 285 | [[package]] 286 | name = "ordermap" 287 | version = "0.2.13" 288 | source = "registry+https://github.com/rust-lang/crates.io-index" 289 | 290 | [[package]] 291 | name = "pear" 292 | version = "0.0.20" 293 | source = "registry+https://github.com/rust-lang/crates.io-index" 294 | 295 | [[package]] 296 | name = "pear_codegen" 297 | version = "0.0.20" 298 | source = "registry+https://github.com/rust-lang/crates.io-index" 299 | dependencies = [ 300 | "version_check 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 301 | "yansi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 302 | ] 303 | 304 | [[package]] 305 | name = "percent-encoding" 306 | version = "1.0.1" 307 | source = "registry+https://github.com/rust-lang/crates.io-index" 308 | 309 | [[package]] 310 | name = "rayon" 311 | version = "0.7.1" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | dependencies = [ 314 | "rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 315 | ] 316 | 317 | [[package]] 318 | name = "rayon-core" 319 | version = "1.4.1" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | dependencies = [ 322 | "crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 323 | "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 324 | "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", 325 | "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 326 | ] 327 | 328 | [[package]] 329 | name = "redox_syscall" 330 | version = "0.1.40" 331 | source = "registry+https://github.com/rust-lang/crates.io-index" 332 | 333 | [[package]] 334 | name = "redox_termios" 335 | version = "0.1.1" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | dependencies = [ 338 | "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", 339 | ] 340 | 341 | [[package]] 342 | name = "regex" 343 | version = "1.0.4" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | dependencies = [ 346 | "aho-corasick 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", 347 | "memchr 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 348 | "regex-syntax 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", 349 | "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 350 | "utf8-ranges 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 351 | ] 352 | 353 | [[package]] 354 | name = "regex-syntax" 355 | version = "0.6.2" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | dependencies = [ 358 | "ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 359 | ] 360 | 361 | [[package]] 362 | name = "ring" 363 | version = "0.11.0" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | dependencies = [ 366 | "gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)", 367 | "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", 368 | "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", 369 | "rayon 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", 370 | "untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", 371 | ] 372 | 373 | [[package]] 374 | name = "rocket" 375 | version = "0.3.16" 376 | source = "registry+https://github.com/rust-lang/crates.io-index" 377 | dependencies = [ 378 | "base64 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", 379 | "cookie 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", 380 | "hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)", 381 | "isatty 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 382 | "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 383 | "memchr 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 384 | "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 385 | "ordermap 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", 386 | "pear 0.0.20 (registry+https://github.com/rust-lang/crates.io-index)", 387 | "pear_codegen 0.0.20 (registry+https://github.com/rust-lang/crates.io-index)", 388 | "smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", 389 | "state 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 390 | "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", 391 | "toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 392 | "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", 393 | "version_check 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 394 | "yansi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 395 | ] 396 | 397 | [[package]] 398 | name = "rocket_codegen" 399 | version = "0.3.16" 400 | source = "registry+https://github.com/rust-lang/crates.io-index" 401 | dependencies = [ 402 | "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 403 | "rocket 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", 404 | "version_check 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 405 | "yansi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 406 | ] 407 | 408 | [[package]] 409 | name = "safemem" 410 | version = "0.2.0" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | 413 | [[package]] 414 | name = "scopeguard" 415 | version = "0.3.3" 416 | source = "registry+https://github.com/rust-lang/crates.io-index" 417 | 418 | [[package]] 419 | name = "serde" 420 | version = "1.0.75" 421 | source = "registry+https://github.com/rust-lang/crates.io-index" 422 | 423 | [[package]] 424 | name = "smallvec" 425 | version = "0.6.5" 426 | source = "registry+https://github.com/rust-lang/crates.io-index" 427 | dependencies = [ 428 | "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 429 | ] 430 | 431 | [[package]] 432 | name = "state" 433 | version = "0.4.1" 434 | source = "registry+https://github.com/rust-lang/crates.io-index" 435 | 436 | [[package]] 437 | name = "strsim" 438 | version = "0.7.0" 439 | source = "registry+https://github.com/rust-lang/crates.io-index" 440 | 441 | [[package]] 442 | name = "termion" 443 | version = "1.5.1" 444 | source = "registry+https://github.com/rust-lang/crates.io-index" 445 | dependencies = [ 446 | "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", 447 | "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", 448 | "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 449 | ] 450 | 451 | [[package]] 452 | name = "textwrap" 453 | version = "0.10.0" 454 | source = "registry+https://github.com/rust-lang/crates.io-index" 455 | dependencies = [ 456 | "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 457 | ] 458 | 459 | [[package]] 460 | name = "thread_local" 461 | version = "0.3.6" 462 | source = "registry+https://github.com/rust-lang/crates.io-index" 463 | dependencies = [ 464 | "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 465 | ] 466 | 467 | [[package]] 468 | name = "time" 469 | version = "0.1.40" 470 | source = "registry+https://github.com/rust-lang/crates.io-index" 471 | dependencies = [ 472 | "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", 473 | "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", 474 | "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", 475 | ] 476 | 477 | [[package]] 478 | name = "toml" 479 | version = "0.4.6" 480 | source = "registry+https://github.com/rust-lang/crates.io-index" 481 | dependencies = [ 482 | "serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)", 483 | ] 484 | 485 | [[package]] 486 | name = "traitobject" 487 | version = "0.1.0" 488 | source = "registry+https://github.com/rust-lang/crates.io-index" 489 | 490 | [[package]] 491 | name = "typeable" 492 | version = "0.1.2" 493 | source = "registry+https://github.com/rust-lang/crates.io-index" 494 | 495 | [[package]] 496 | name = "ucd-util" 497 | version = "0.1.1" 498 | source = "registry+https://github.com/rust-lang/crates.io-index" 499 | 500 | [[package]] 501 | name = "unicase" 502 | version = "1.4.2" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | dependencies = [ 505 | "version_check 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 506 | ] 507 | 508 | [[package]] 509 | name = "unicode-bidi" 510 | version = "0.3.4" 511 | source = "registry+https://github.com/rust-lang/crates.io-index" 512 | dependencies = [ 513 | "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 514 | ] 515 | 516 | [[package]] 517 | name = "unicode-normalization" 518 | version = "0.1.7" 519 | source = "registry+https://github.com/rust-lang/crates.io-index" 520 | 521 | [[package]] 522 | name = "unicode-width" 523 | version = "0.1.5" 524 | source = "registry+https://github.com/rust-lang/crates.io-index" 525 | 526 | [[package]] 527 | name = "unreachable" 528 | version = "1.0.0" 529 | source = "registry+https://github.com/rust-lang/crates.io-index" 530 | dependencies = [ 531 | "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 532 | ] 533 | 534 | [[package]] 535 | name = "untrusted" 536 | version = "0.5.1" 537 | source = "registry+https://github.com/rust-lang/crates.io-index" 538 | 539 | [[package]] 540 | name = "url" 541 | version = "1.7.1" 542 | source = "registry+https://github.com/rust-lang/crates.io-index" 543 | dependencies = [ 544 | "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 545 | "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 546 | "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 547 | ] 548 | 549 | [[package]] 550 | name = "utf8-ranges" 551 | version = "1.0.1" 552 | source = "registry+https://github.com/rust-lang/crates.io-index" 553 | 554 | [[package]] 555 | name = "vec_map" 556 | version = "0.8.1" 557 | source = "registry+https://github.com/rust-lang/crates.io-index" 558 | 559 | [[package]] 560 | name = "version_check" 561 | version = "0.1.4" 562 | source = "registry+https://github.com/rust-lang/crates.io-index" 563 | 564 | [[package]] 565 | name = "void" 566 | version = "1.0.2" 567 | source = "registry+https://github.com/rust-lang/crates.io-index" 568 | 569 | [[package]] 570 | name = "winapi" 571 | version = "0.3.5" 572 | source = "registry+https://github.com/rust-lang/crates.io-index" 573 | dependencies = [ 574 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 575 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 576 | ] 577 | 578 | [[package]] 579 | name = "winapi-i686-pc-windows-gnu" 580 | version = "0.4.0" 581 | source = "registry+https://github.com/rust-lang/crates.io-index" 582 | 583 | [[package]] 584 | name = "winapi-x86_64-pc-windows-gnu" 585 | version = "0.4.0" 586 | source = "registry+https://github.com/rust-lang/crates.io-index" 587 | 588 | [[package]] 589 | name = "yansi" 590 | version = "0.4.0" 591 | source = "registry+https://github.com/rust-lang/crates.io-index" 592 | 593 | [metadata] 594 | "checksum aho-corasick 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "68f56c7353e5a9547cbd76ed90f7bb5ffc3ba09d4ea9bd1d8c06c8b1142eeb5a" 595 | "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" 596 | "checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" 597 | "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" 598 | "checksum base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96434f987501f0ed4eb336a411e0631ecd1afa11574fe148587adc4ff96143c9" 599 | "checksum base64 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "85415d2594767338a74a30c1d370b2f3262ec1b4ed2d7bba5b3faf4de40467d9" 600 | "checksum bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c54bb8f454c567f21197eefcdbf5679d0bd99f2ddbe52e84c77061952e6789" 601 | "checksum byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "90492c5858dd7d2e78691cfb89f90d273a2800fc11d98f60786e5d87e2f83781" 602 | "checksum cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4e7bb64a8ebb0d856483e1e682ea3422f883c5f5615a90d51a2c82fe87fdd3" 603 | "checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878" 604 | "checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e" 605 | "checksum cookie 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "477eb650753e319be2ae77ec368a58c638f9f0c4d941c39bad95e950fb1d1d0d" 606 | "checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3" 607 | "checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150" 608 | "checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" 609 | "checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb" 610 | "checksum httparse 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7b6288d7db100340ca12873fd4d08ad1b8f206a9457798dfb17c018a33fee540" 611 | "checksum hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)" = "368cb56b2740ebf4230520e2b90ebb0461e69034d85d1945febd9b3971426db2" 612 | "checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" 613 | "checksum isatty 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6c324313540cd4d7ba008d43dc6606a32a5579f13cc17b2804c13096f0a5c522" 614 | "checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" 615 | "checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" 616 | "checksum lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca488b89a5657b0a2ecd45b95609b3e848cf1755da332a0da46e2b2b1cb371a7" 617 | "checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d" 618 | "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" 619 | "checksum log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cba860f648db8e6f269df990180c2217f333472b4a6e901e97446858487971e2" 620 | "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" 621 | "checksum memchr 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a3b4142ab8738a78c51896f704f83c11df047ff1bda9a92a661aa6361552d93d" 622 | "checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" 623 | "checksum mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0" 624 | "checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" 625 | "checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" 626 | "checksum num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "630de1ef5cc79d0cdd78b7e33b81f083cbfe90de0f4b2b2f07f905867c70e9fe" 627 | "checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" 628 | "checksum ordermap 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "b81cf3b8cb96aa0e73bbedfcdc9708d09fec2854ba8d474be4e6f666d7379e8b" 629 | "checksum pear 0.0.20 (registry+https://github.com/rust-lang/crates.io-index)" = "353fe88ff7a430c0f39ca4ec19e1f8fa0062f696370e8df3080ac40139a63301" 630 | "checksum pear_codegen 0.0.20 (registry+https://github.com/rust-lang/crates.io-index)" = "fd0f3ef1db2d855e0c00fad8e5a8216a70df6d9c1c7f7a7ac9f1cf50675142b7" 631 | "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" 632 | "checksum rayon 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a77c51c07654ddd93f6cb543c7a849863b03abc7e82591afda6dc8ad4ac3ac4a" 633 | "checksum rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b055d1e92aba6877574d8fe604a63c8b5df60f60e5982bf7ccbb1338ea527356" 634 | "checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" 635 | "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" 636 | "checksum regex 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "67d0301b0c6804eca7e3c275119d0b01ff3b7ab9258a65709e608a66312a1025" 637 | "checksum regex-syntax 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "747ba3b235651f6e2f67dfa8bcdcd073ddb7c243cb21c442fc12395dfcac212d" 638 | "checksum ring 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1f2a6dc7fc06a05e6de183c5b97058582e9da2de0c136eafe49609769c507724" 639 | "checksum rocket 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4dc32be1d093e7b2f9718983318c6bf5a14f43d7ea01a0b5143c3450c90725b9" 640 | "checksum rocket_codegen 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "bfc94e7781a8bc502f3614521ae94b562f209c7537671cb6169cbbe9dbcc6c5e" 641 | "checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f" 642 | "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" 643 | "checksum serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)" = "22d340507cea0b7e6632900a176101fea959c7065d93ba555072da90aaaafc87" 644 | "checksum smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "153ffa32fd170e9944f7e0838edf824a754ec4c1fc64746fcc9fe1f8fa602e5d" 645 | "checksum state 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7345c971d1ef21ffdbd103a75990a15eb03604fc8b8852ca8cb418ee1a099028" 646 | "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" 647 | "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" 648 | "checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6" 649 | "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" 650 | "checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b" 651 | "checksum toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a0263c6c02c4db6c8f7681f9fd35e90de799ebd4cfdeab77a38f4ff6b3d8c0d9" 652 | "checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" 653 | "checksum typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" 654 | "checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d" 655 | "checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" 656 | "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" 657 | "checksum unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25" 658 | "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" 659 | "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" 660 | "checksum untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f392d7819dbe58833e26872f5f6f0d68b7bbbe90fc3667e98731c4a15ad9a7ae" 661 | "checksum url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2a321979c09843d272956e73700d12c4e7d3d92b2ee112b31548aef0d4efc5a6" 662 | "checksum utf8-ranges 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd70f467df6810094968e2fce0ee1bd0e87157aceb026a8c083bcf5e25b9efe4" 663 | "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" 664 | "checksum version_check 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7716c242968ee87e5542f8021178248f267f295a5c4803beae8b8b7fd9bc6051" 665 | "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 666 | "checksum winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "773ef9dcc5f24b7d850d0ff101e542ff24c3b090a9768e03ff889fdef41f00fd" 667 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 668 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 669 | "checksum yansi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d60c3b48c9cdec42fb06b3b84b5b087405e1fa1c644a1af3930e4dfafe93de48" 670 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "myslowparser" 3 | version = "1.1.7" 4 | authors = ["Alexander Kozharsky "] 5 | 6 | [dependencies] 7 | regex = "1.0.4" 8 | lazy_static = "1.1.0" 9 | chrono = "0.4.6" 10 | clap = "2.32.0" 11 | rocket = "0.3.16" 12 | rocket_codegen = "0.3.16" 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2018, Southbridge LLC 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # myslowparser 2 | MySQL slow log parser 3 | 4 | ```MySQL slow log parser 1.1.5 5 | Developed by Alexander Kozharsky 6 | Copyright (c) Southbridge, LLC https://southbridge.io 7 | Parses MySQL slow log very fast 8 | 9 | USAGE: 10 | myslowparser [FLAGS] [OPTIONS] 11 | 12 | FLAGS: 13 | -a, --abstract Abstact strings to |STRING|, numbers to |NUMBER| 14 | -d, --dedup Remove query duplicates. Shows only last query 15 | -h, --help Prints help information 16 | -p, --print_cfg Print current configuration 17 | -V, --version Prints version information 18 | 19 | OPTIONS: 20 | --cnt_max Query count maximum value 21 | --cnt_min Query count minimum value 22 | --database Database name 23 | -f, --file Path to file to parse 24 | -l, --limit Limit to first queries 25 | --lt_max Lock time maximum value 26 | --lt_min Lock time minimum value 27 | --qt_max Query time maximum value 28 | --qt_min Query time minimum value 29 | -r, --query_regex Query regex filter 30 | --ra_max Rows affected maximum value 31 | --ra_min Rows affected minimum value 32 | --re_max Rows examined maximum value 33 | --re_min Rows examined minimum value 34 | --rs_max Rows sent maximum value 35 | --rs_min Rows sent minimum value 36 | -s, --sort_type Sort by column parameter, where SORT_TYPE: 37 | ts - Timestamp 38 | qt - Query time 39 | lt - Lock time 40 | rs - Rows sent 41 | re - Rows examined 42 | ra - Rows affected 43 | tsi - Timestamp inverse 44 | qti - Query time inverse 45 | lti - Lock time inverse 46 | rsi - Rows sent inverse 47 | rei - Rows examined inverse 48 | rai - Rows affected inverse 49 | cnt - Count 50 | cnti - Count inverse 51 | --ts_max Timestamp range maximum value 52 | format: Unix timestamp or DD/MM/YYYY 53 | --ts_min Timestamp range minimum value 54 | format: Unix timestamp or DD/MM/YYYY 55 | -w, --web Run web server on 56 | If ADDR omitted, then listen on 127.0.0.1 57 | Port 0 (zero) to disable feature (disabled by default) 58 | ``` 59 | -------------------------------------------------------------------------------- /myslowparser.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(plugin)] 2 | #![plugin(rocket_codegen)] 3 | 4 | #[macro_use] extern crate lazy_static; 5 | extern crate regex; 6 | extern crate chrono; 7 | extern crate clap; 8 | extern crate rocket; 9 | 10 | mod regs; 11 | mod types; 12 | mod processing; 13 | mod web; 14 | 15 | use std::fs::File; 16 | use std::io::{BufReader, Read}; 17 | use std::sync::Mutex; 18 | use chrono::TimeZone; 19 | use chrono::prelude::Utc; 20 | use clap::{App, Arg}; 21 | use types::{Query, Config, QueriesSortType}; 22 | use regex::Regex; 23 | use std::thread::sleep; 24 | use std::time::Duration; 25 | 26 | lazy_static! { 27 | static ref queries: Mutex> = Mutex::new(Vec::new()); 28 | } 29 | 30 | lazy_static! { 31 | static ref config: Mutex = Mutex::new(Config::new()); 32 | } 33 | 34 | fn main() { 35 | match configure() { 36 | Err(err) => { 37 | println!("Can't continue due errors:\n{}", err); 38 | return; 39 | } 40 | _ => {} 41 | } 42 | 43 | read_queries(false); 44 | 45 | { 46 | let mut qq = queries.lock().unwrap(); 47 | processing::process(&mut qq, false); 48 | } 49 | 50 | { 51 | if config.lock().unwrap().web_port > 0 { 52 | web::invoke_web(); 53 | } 54 | } 55 | } 56 | 57 | fn read_queries(background: bool) { 58 | let log_file = { 59 | let cnf = config.lock().unwrap(); 60 | 61 | cnf.log_file.clone() 62 | }; 63 | 64 | let wdelay = config.lock().unwrap().wpd; 65 | 66 | let file = match File::open(&log_file) { 67 | Ok(file) => file, 68 | Err(err) => { 69 | eprintln!("Can't open file {}: {}", log_file, err); 70 | return; 71 | } 72 | }; 73 | 74 | let mut buf: [u8; 1] = [0]; 75 | let mut reader = BufReader::new(&file); 76 | let mut line = String::new(); 77 | let mut new_query = Query::new(); 78 | let mut server_info = String::new(); 79 | let mut server_info_consumed = false; 80 | 81 | while let Ok(_) = reader.read_exact(&mut buf) { 82 | if buf[0] == 0x0A { 83 | if !server_info_consumed { 84 | if !regs::is_info(&line) { 85 | server_info += &line; 86 | server_info.push_str("\n"); 87 | line = String::new(); 88 | continue; 89 | } else { 90 | server_info_consumed = true; 91 | } 92 | } 93 | 94 | if regs::is_info(&line) { 95 | handle_info(&line, &mut new_query); 96 | } else { 97 | handle_raw(&line, &mut new_query); 98 | } 99 | 100 | if new_query.valid() { 101 | let mut qq = queries.lock().unwrap(); 102 | let query_index = qq.len(); 103 | new_query.finish(); 104 | 105 | if regs::is_query_end(&new_query.query) { 106 | qq.insert(query_index, new_query); 107 | } 108 | 109 | if background { 110 | sleep(wdelay); 111 | } 112 | 113 | new_query = Query::new(); 114 | } 115 | 116 | line = String::new(); 117 | } else { 118 | line += &String::from_utf8_lossy(&buf); 119 | } 120 | } 121 | 122 | if server_info.len() > 0 && !background { 123 | let si_split: Vec<&str> = server_info.split("\n").collect(); 124 | let info_string = si_split[0].to_string(); 125 | println!("SERVER INFO: {}\n", info_string.replace(". started with:", "")); 126 | } 127 | } 128 | 129 | fn configure() -> Result<(), String> { 130 | let mut cnf = config.lock().unwrap(); 131 | 132 | let matches = App::new("MySQL slow log parser") 133 | .version("1.1.7") 134 | .author("Developed by Alexander Kozharsky 135 | Copyright (c) Southbridge, LLC https://southbridge.io") 136 | .about("Parses MySQL slow log very fast") 137 | .arg(Arg::with_name("file") 138 | .short("f") 139 | .long("file") 140 | .value_name("FILE") 141 | .help("Path to file to parse")) 142 | .arg(Arg::with_name("ts_min") 143 | .long("ts_min") 144 | .value_name("TIMESTAMP_MIN") 145 | .help("Timestamp range minimum value 146 | format: Unix timestamp or DD/MM/YYYY")) 147 | .arg(Arg::with_name("ts_max") 148 | .long("ts_max") 149 | .value_name("TIMESTAMP_MAX") 150 | .help("Timestamp range maximum value 151 | format: Unix timestamp or DD/MM/YYYY")) 152 | .arg(Arg::with_name("database") 153 | .long("database") 154 | .value_name("DATABASE") 155 | .help("Database name")) 156 | .arg(Arg::with_name("qt_min") 157 | .long("qt_min") 158 | .value_name("QUERY_TIME_MIN") 159 | .help("Query time minimum value")) 160 | .arg(Arg::with_name("qt_max") 161 | .long("qt_max") 162 | .value_name("QUERY_TIME_MAX") 163 | .help("Query time maximum value")) 164 | .arg(Arg::with_name("lt_min") 165 | .long("lt_min") 166 | .value_name("LOCK_TIME_MIN") 167 | .help("Lock time minimum value")) 168 | .arg(Arg::with_name("lt_max") 169 | .long("lt_max") 170 | .value_name("LOCK_TIME_MAX") 171 | .help("Lock time maximum value")) 172 | .arg(Arg::with_name("rs_min") 173 | .long("rs_min") 174 | .value_name("ROWS_SENT_MIN") 175 | .help("Rows sent minimum value")) 176 | .arg(Arg::with_name("rs_max") 177 | .long("rs_max") 178 | .value_name("ROWS_SENT_MAX") 179 | .help("Rows sent maximum value")) 180 | .arg(Arg::with_name("re_min") 181 | .long("re_min") 182 | .value_name("ROWS_EXAMINED_MIN") 183 | .help("Rows examined minimum value")) 184 | .arg(Arg::with_name("re_max") 185 | .long("re_max") 186 | .value_name("ROWS_EXAMINED_MAX") 187 | .help("Rows examined maximum value")) 188 | .arg(Arg::with_name("ra_min") 189 | .long("ra_min") 190 | .value_name("ROWS_AFFECTED_MIN") 191 | .help("Rows affected minimum value")) 192 | .arg(Arg::with_name("ra_max") 193 | .long("ra_max") 194 | .value_name("ROWS_AFFECTED_MAX") 195 | .help("Rows affected maximum value")) 196 | .arg(Arg::with_name("sort_type") 197 | .short("s") 198 | .long("sort_type") 199 | .value_name("SORT_TYPE") 200 | .help("Sort by column parameter, where SORT_TYPE: 201 | ts - Timestamp 202 | qt - Query time 203 | lt - Lock time 204 | rs - Rows sent 205 | re - Rows examined 206 | ra - Rows affected 207 | tsi - Timestamp inverse 208 | qti - Query time inverse 209 | lti - Lock time inverse 210 | rsi - Rows sent inverse 211 | rei - Rows examined inverse 212 | rai - Rows affected inverse 213 | cnt - Count 214 | cnti - Count inverse")) 215 | .arg(Arg::with_name("query_regex") 216 | .short("r") 217 | .long("query_regex") 218 | .value_name("REGEX_STRING") 219 | .help("Query regex filter")) 220 | .arg(Arg::with_name("cnt_min") 221 | .long("cnt_min") 222 | .value_name("COUNT_MIN") 223 | .help("Query count minimum value")) 224 | .arg(Arg::with_name("cnt_max") 225 | .long("cnt_max") 226 | .value_name("COUNT_MAX") 227 | .help("Query count maximum value")) 228 | .arg(Arg::with_name("limit") 229 | .short("l") 230 | .long("limit") 231 | .value_name("LIMIT") 232 | .help("Limit to first queries")) 233 | .arg(Arg::with_name("abstract") 234 | .short("a") 235 | .long("abstract") 236 | .help("Abstact strings to |STRING|, numbers to |NUMBER|")) 237 | .arg(Arg::with_name("print_cfg") 238 | .short("p") 239 | .long("print_cfg") 240 | .multiple(true) 241 | .help("Print current configuration")) 242 | .arg(Arg::with_name("web") 243 | .short("w") 244 | .long("web") 245 | .value_name("ADDR:PORT") 246 | .help("Run web server on 247 | If ADDR omitted, then listen on 127.0.0.1 248 | Port 0 (zero) to disable feature (disabled by default)")) 249 | .arg(Arg::with_name("dedup") 250 | .short("d") 251 | .long("dedup") 252 | .help("Remove query duplicates. Shows only last query")) 253 | .arg(Arg::with_name("wpd") 254 | .long("wpd") 255 | .value_name("MILLIS") 256 | .help("Set queries background parse delay in web mode")) 257 | .get_matches(); 258 | 259 | cnf.log_file = matches.value_of("file").unwrap_or("mysql-slow.log").to_string(); 260 | 261 | if let Ok(ts_min) = matches.value_of("ts_min").unwrap_or("-1").parse::() { 262 | cnf.timestamp_begin = ts_min; 263 | } else { 264 | let ts_min_date = matches.value_of("ts_min").unwrap_or("15/12/1901").to_string(); 265 | let datereg = Regex::new(r"^(?P
\d{2})[/\-.](?P\d{2})[/\-.](?P\d{4})$").unwrap(); 266 | 267 | if let Some(datecapts) = datereg.captures(&ts_min_date) { 268 | let datestr = format!("{}/{}/{}:00:00:00", 269 | &datecapts["dd"].to_string(), 270 | &datecapts["mm"].to_string(), 271 | &datecapts["yyyy"].to_string()); 272 | 273 | let date = Utc.datetime_from_str(&datestr, Query::DT_FORMAT).unwrap(); 274 | cnf.timestamp_begin = date.timestamp(); 275 | } else { 276 | cnf.add_error("Timestamp range minimum value invalid syntax"); 277 | } 278 | } 279 | 280 | if let Ok(ts_max) = matches.value_of("ts_max").unwrap_or("-1").parse::() { 281 | cnf.timestamp_end = if cnf.timestamp_begin > ts_max || ts_max < 0 282 | { std::i64::MAX } else { ts_max }; 283 | } else { 284 | let ts_max_date = matches.value_of("ts_max").unwrap_or("14/12/1901").to_string(); 285 | let datereg = Regex::new(r"^(?P
\d{2})[/\-.](?P\d{2})[/\-.](?P\d{4})$").unwrap(); 286 | 287 | if let Some(datecapts) = datereg.captures(&ts_max_date) { 288 | let datestr = format!("{}/{}/{}:23:59:59", 289 | &datecapts["dd"].to_string(), 290 | &datecapts["mm"].to_string(), 291 | &datecapts["yyyy"].to_string()); 292 | 293 | let date = Utc.datetime_from_str(&datestr, Query::DT_FORMAT).unwrap(); 294 | cnf.timestamp_end = date.timestamp(); 295 | 296 | if cnf.timestamp_end < cnf.timestamp_begin { 297 | cnf.timestamp_end = std::i64::MAX; 298 | } 299 | } else { 300 | cnf.add_error("Timestamp range maximum value invalid syntax"); 301 | } 302 | } 303 | 304 | cnf.db = matches.value_of("database").unwrap_or("").to_string(); 305 | 306 | if let Ok(qt_min) = matches.value_of("qt_min").unwrap_or("-1").parse::() { 307 | cnf.query_time_min = qt_min; 308 | } else { 309 | cnf.add_error("Query time range minimum value invalid syntax"); 310 | } 311 | 312 | if let Ok(qt_max) = matches.value_of("qt_max").unwrap_or("-1").parse::() { 313 | cnf.query_time_max = if cnf.query_time_min > qt_max || qt_max < 0.0 314 | { std::f64::MAX } else { qt_max }; 315 | } else { 316 | cnf.add_error("Query time range maximum value invalid syntax"); 317 | } 318 | 319 | if let Ok(lt_min) = matches.value_of("lt_min").unwrap_or("-1").parse::() { 320 | cnf.lock_time_min = lt_min; 321 | } else { 322 | cnf.add_error("Lock time range minimum value invalid syntax"); 323 | } 324 | 325 | if let Ok(lt_max) = matches.value_of("lt_max").unwrap_or("-1").parse::() { 326 | cnf.lock_time_max = if cnf.lock_time_min > lt_max || lt_max < 0.0 327 | { std::f64::MAX } else { lt_max }; 328 | } else { 329 | cnf.add_error("Lock time range maximum value invalid syntax"); 330 | } 331 | 332 | if let Ok(rs_min) = matches.value_of("rs_min").unwrap_or("-1").parse::() { 333 | cnf.rows_sent_min = rs_min; 334 | } else { 335 | cnf.add_error("Rows sent range minimum value invalid syntax"); 336 | } 337 | 338 | if let Ok(rs_max) = matches.value_of("rs_max").unwrap_or("-1").parse::() { 339 | cnf.rows_sent_max = if cnf.rows_sent_min > rs_max || rs_max < 0 340 | { std::i64::MAX } else { rs_max }; 341 | } else { 342 | cnf.add_error("Rows sent range maximum value invalid syntax"); 343 | } 344 | 345 | if let Ok(re_min) = matches.value_of("re_min").unwrap_or("-1").parse::() { 346 | cnf.rows_examined_min = re_min; 347 | } else { 348 | cnf.add_error("Rows examined range minimum value invalid syntax"); 349 | } 350 | 351 | if let Ok(re_max) = matches.value_of("re_max").unwrap_or("-1").parse::() { 352 | cnf.rows_examined_max = if cnf.rows_examined_min > re_max || re_max < 0 353 | { std::i64::MAX } else { re_max }; 354 | } else { 355 | cnf.add_error("Rows examined range maximum value invalid syntax"); 356 | } 357 | 358 | if let Ok(ra_min) = matches.value_of("ra_min").unwrap_or("-1").parse::() { 359 | cnf.rows_affected_min = ra_min; 360 | } else { 361 | cnf.add_error("Rows affected range minimum value invalid syntax"); 362 | } 363 | 364 | if let Ok(ra_max) = matches.value_of("ra_max").unwrap_or("-1").parse::() { 365 | cnf.rows_affected_max = if cnf.rows_affected_min > ra_max || ra_max < 0 366 | { std::i64::MAX } else { ra_max }; 367 | } else { 368 | cnf.add_error("Rows affected range maximum value invalid syntax"); 369 | } 370 | 371 | if let Ok(cnt_min) = matches.value_of("cnt_min").unwrap_or("0").parse::() { 372 | cnf.count_min = cnt_min; 373 | } else { 374 | cnf.add_error("Count range minimum value invalid syntax"); 375 | } 376 | 377 | if let Ok(cnt_max) = matches.value_of("cnt_max").unwrap_or("0").parse::() { 378 | cnf.count_max = if cnf.count_min > cnt_max || cnt_max == 0 379 | { std::usize::MAX } else { cnt_max }; 380 | } else { 381 | cnf.add_error("Count range maximum value invalid syntax"); 382 | } 383 | 384 | if let Ok(limit) = matches.value_of("limit").unwrap_or("0").parse::() { 385 | cnf.limit = if limit > 0 { limit - 1 } else { std::usize::MAX }; 386 | } else { 387 | cnf.add_error("Limit value invalid syntax"); 388 | } 389 | 390 | if let Some(regex_string) = matches.value_of("query_regex") { 391 | if let Ok(regex_value) = Regex::new(regex_string) { 392 | cnf.regex = Some(regex_value); 393 | } else { 394 | cnf.add_error("Invalid query regex provided"); 395 | } 396 | } 397 | 398 | if let Ok(wpd) = matches.value_of("wpd").unwrap_or("1").parse::() { 399 | cnf.wpd = Duration::from_millis(wpd); 400 | } else { 401 | cnf.add_error("Web parse delay syntax error"); 402 | } 403 | 404 | let web = matches.value_of("web").unwrap_or("0").to_string(); 405 | 406 | if web != "0" { 407 | if let Some(addr_port_capts) = regs::addr_port(&web) { 408 | cnf.web_addr = addr_port_capts["addr"].to_string(); 409 | cnf.web_port = addr_port_capts["port"].parse::().unwrap(); 410 | } else if let Ok(web_port) = web.parse::() { 411 | cnf.web_addr = "127.0.0.1".to_string(); 412 | cnf.web_port = web_port; 413 | } else { 414 | cnf.add_error("Invalid web address:port pair provided"); 415 | } 416 | } 417 | 418 | cnf.abs = matches.occurrences_of("abstract") > 0; 419 | cnf.dedup = matches.occurrences_of("dedup") > 0; 420 | 421 | let sort_type = &*matches.value_of("sort_type").unwrap_or("ts").to_string(); 422 | 423 | cnf.sort_type = { 424 | match sort_type { 425 | "ts" => QueriesSortType::Timestamp, 426 | "qt" => QueriesSortType::QueryTime, 427 | "lt" => QueriesSortType::LockTime, 428 | "rs" => QueriesSortType::RowsSent, 429 | "re" => QueriesSortType::RowsExamined, 430 | "ra" => QueriesSortType::RowsAffected, 431 | "tsi" => QueriesSortType::TimestampInverse, 432 | "qti" => QueriesSortType::QueryTimeInverse, 433 | "lti" => QueriesSortType::LockTimeInverse, 434 | "rsi" => QueriesSortType::RowsSentInverse, 435 | "rei" => QueriesSortType::RowsExaminedInverse, 436 | "rai" => QueriesSortType::RowsAffectedInverse, 437 | "cnt" => QueriesSortType::Count, 438 | "cnti" => QueriesSortType::CountInverse, 439 | _ => { 440 | cnf.add_error("Sort type invalid"); 441 | QueriesSortType::Undefined 442 | } 443 | } 444 | }; 445 | 446 | let print_matches = matches.occurrences_of("print_cfg"); 447 | 448 | if print_matches > 0 { 449 | println!("{}\n", cnf.to_string()); 450 | 451 | if print_matches > 1 { 452 | cnf.add_error("Interrupted by -pp flag. To disable interrupt, try -p flag"); 453 | } 454 | } 455 | 456 | if cnf.has_errors() { 457 | Err(cnf.errors()) 458 | } else { 459 | Ok(()) 460 | } 461 | } 462 | 463 | fn handle_info(line: &String, query: &mut Query) { 464 | if let Some(time) = regs::date_time(&line) { 465 | let time_str = format!("{}/{}/{}:{}:{}:{}", 466 | &time["day"], &time["month"], &time["year"], 467 | &time["hour"], &time["minute"], &time["second"]); 468 | 469 | if let Ok(time) = Utc.datetime_from_str(&time_str, Query::DT_FORMAT) { 470 | query.timestamp = time.timestamp(); 471 | } 472 | } 473 | 474 | if let Some(schema) = regs::schema(&line) { 475 | query.db = schema["schema"].to_string(); 476 | } 477 | 478 | if let Some(query_time) = regs::query_time(&line) { 479 | let query_time_str = query_time["query_time"].to_string(); 480 | 481 | if let Ok(query_time) = query_time_str.parse::() { 482 | query.query_time = query_time; 483 | } 484 | } 485 | 486 | if let Some(lock_time) = regs::lock_time(&line) { 487 | let lock_time_str = lock_time["lock_time"].to_string(); 488 | 489 | if let Ok(lock_time) = lock_time_str.parse::() { 490 | query.lock_time = lock_time; 491 | } 492 | } 493 | 494 | if let Some(rows_sent) = regs::rows_sent(&line) { 495 | let rows_sent_str = rows_sent["rows_sent"].to_string(); 496 | 497 | if let Ok(rows_sent) = rows_sent_str.parse::() { 498 | query.rows_sent = rows_sent; 499 | } 500 | } 501 | 502 | if let Some(rows_examined) = regs::rows_examined(&line) { 503 | let rows_examined_str = rows_examined["rows_examined"].to_string(); 504 | 505 | if let Ok(rows_examined) = rows_examined_str.parse::() { 506 | query.rows_examined = rows_examined; 507 | } 508 | } 509 | 510 | if let Some(rows_affected) = regs::rows_affected(&line) { 511 | let rows_affected_str = rows_affected["rows_affected"].to_string(); 512 | 513 | if let Ok(rows_affected) = rows_affected_str.parse::() { 514 | query.rows_affected = rows_affected; 515 | } 516 | } 517 | } 518 | 519 | fn handle_raw(line: &String, query: &mut Query) { 520 | let mut dirty = false; 521 | 522 | if let Some(db) = regs::db(&line) { 523 | query.db = db["db"].to_string(); 524 | dirty = true; 525 | } 526 | 527 | if let Some(timestamp) = regs::timestamp(&line) { 528 | let timestamp_str = timestamp["timestamp"].to_string(); 529 | 530 | if let Ok(timestamp) = timestamp_str.parse::() { 531 | query.timestamp = timestamp; 532 | } 533 | 534 | dirty = true; 535 | } 536 | 537 | if !dirty { 538 | if !query.consuming_query { 539 | query.query = String::new(); 540 | query.consuming_query = true; 541 | } 542 | 543 | query.query += &*regs::remove_comments(line); 544 | } 545 | 546 | if query.consuming_query && regs::is_query_end(line) { 547 | let cnf = config.lock().unwrap(); 548 | 549 | if cnf.abs { 550 | query.query = regs::abs_numbers(&query.query); 551 | query.query = regs::abs_strings(&query.query); 552 | } 553 | 554 | query.query = regs::prs_spaces_trim(&query.query); 555 | query.query_consumed = true; 556 | } 557 | } 558 | -------------------------------------------------------------------------------- /src/processing.rs: -------------------------------------------------------------------------------- 1 | use types::Query; 2 | use super::config; 3 | use types::QueriesSortType; 4 | use std::collections::HashMap; 5 | use web::wqq; 6 | use std::sync::Mutex; 7 | use std::thread::sleep; 8 | 9 | lazy_static! { 10 | pub static ref qhash: Mutex> = Mutex::new(HashMap::new()); 11 | } 12 | 13 | fn sort(qq: &mut Vec) { 14 | let cnf = config.lock().unwrap(); 15 | 16 | match cnf.sort_type { 17 | QueriesSortType::Timestamp => 18 | qq.sort_by(|lhs, rhs| 19 | lhs.timestamp.partial_cmp(&rhs.timestamp).unwrap()), 20 | 21 | QueriesSortType::QueryTime => 22 | qq.sort_by(|lhs, rhs| 23 | lhs.query_time.partial_cmp(&rhs.query_time).unwrap()), 24 | 25 | QueriesSortType::LockTime => 26 | qq.sort_by(|lhs, rhs| 27 | lhs.lock_time.partial_cmp(&rhs.lock_time).unwrap()), 28 | 29 | QueriesSortType::RowsSent => 30 | qq.sort_by(|lhs, rhs| 31 | lhs.rows_sent.partial_cmp(&rhs.rows_sent).unwrap()), 32 | 33 | QueriesSortType::RowsExamined => 34 | qq.sort_by(|lhs, rhs| 35 | lhs.rows_examined.partial_cmp(&rhs.rows_examined).unwrap()), 36 | 37 | QueriesSortType::RowsAffected => 38 | qq.sort_by(|lhs, rhs| 39 | lhs.rows_affected.partial_cmp(&rhs.rows_affected).unwrap()), 40 | 41 | QueriesSortType::TimestampInverse => 42 | qq.sort_by(|lhs, rhs| 43 | rhs.timestamp.partial_cmp(&lhs.timestamp).unwrap()), 44 | 45 | QueriesSortType::QueryTimeInverse => 46 | qq.sort_by(|lhs, rhs| 47 | rhs.query_time.partial_cmp(&lhs.query_time).unwrap()), 48 | 49 | QueriesSortType::LockTimeInverse => 50 | qq.sort_by(|lhs, rhs| 51 | rhs.lock_time.partial_cmp(&lhs.lock_time).unwrap()), 52 | 53 | QueriesSortType::RowsSentInverse => 54 | qq.sort_by(|lhs, rhs| 55 | rhs.rows_sent.partial_cmp(&lhs.rows_sent).unwrap()), 56 | 57 | QueriesSortType::RowsExaminedInverse => 58 | qq.sort_by(|lhs, rhs| 59 | rhs.rows_examined.partial_cmp(&lhs.rows_examined).unwrap()), 60 | 61 | QueriesSortType::RowsAffectedInverse => 62 | qq.sort_by(|lhs, rhs| 63 | rhs.rows_affected.partial_cmp(&lhs.rows_affected).unwrap()), 64 | 65 | _ => {} 66 | } 67 | } 68 | 69 | fn filter(qq: &Vec, mapflt: &mut usize) -> Vec { 70 | let cnf = config.lock().unwrap(); 71 | 72 | qq.into_iter().filter(|q| { 73 | let not_filtered = q.timestamp >= cnf.timestamp_begin && 74 | q.timestamp < cnf.timestamp_end && 75 | q.query_time >= cnf.query_time_min && 76 | q.query_time < cnf.query_time_max && 77 | q.lock_time >= cnf.lock_time_min && 78 | q.lock_time < cnf.lock_time_max && 79 | q.rows_sent >= cnf.rows_sent_min && 80 | q.rows_sent < cnf.rows_sent_max && 81 | q.rows_examined >= cnf.rows_examined_min && 82 | q.rows_examined < cnf.rows_examined_max && 83 | q.rows_affected >= cnf.rows_affected_min && 84 | q.rows_affected < cnf.rows_affected_max; 85 | 86 | if not_filtered { 87 | if let Some(regex) = &cnf.regex { 88 | let not_filter = !regex.find(&q.query).is_none(); 89 | 90 | if !not_filter { 91 | *mapflt += 1; 92 | } 93 | 94 | return not_filter; 95 | } 96 | } 97 | 98 | if !not_filtered { 99 | *mapflt += 1; 100 | } 101 | 102 | not_filtered 103 | }).cloned().collect() 104 | } 105 | 106 | fn make_dedup_hash(qq: &Vec) -> HashMap<&String, &Query> { 107 | let mut dedup_hash: HashMap<&String, &Query> = HashMap::new(); 108 | 109 | for q in qq.iter() { 110 | if !dedup_hash.get(&q.query).is_none() { 111 | dedup_hash.remove(&q.query); 112 | } 113 | 114 | dedup_hash.insert(&q.query, q); 115 | } 116 | 117 | dedup_hash 118 | } 119 | 120 | pub fn process(qq: &mut Vec, web: bool) { 121 | let mut mapflt: usize = 0; 122 | let mut queries_hash = qhash.lock().unwrap(); 123 | let wdelay = config.lock().unwrap().wpd; 124 | 125 | queries_hash.clear(); 126 | 127 | for q in qq.iter() { 128 | let count = queries_hash.entry(q.query.clone()).or_insert(0); 129 | *count += 1; 130 | sleep(wdelay); 131 | } 132 | 133 | 134 | let mut new_qq = { 135 | if config.lock().unwrap().dedup { 136 | let mut dedup_hash = make_dedup_hash(&qq); 137 | let mut dedupd_qq: Vec = Vec::new(); 138 | 139 | for (_, &q) in dedup_hash.iter() { 140 | dedupd_qq.push(q.clone()); 141 | } 142 | 143 | filter(&dedupd_qq, &mut mapflt) 144 | } else { 145 | filter(qq, &mut mapflt) 146 | } 147 | }; 148 | 149 | sort(&mut new_qq); 150 | let cnf = config.lock().unwrap(); 151 | 152 | { 153 | match cnf.sort_type { 154 | QueriesSortType::Count => 155 | new_qq.sort_by(|lhs, rhs| 156 | (*queries_hash.get(&lhs.query).unwrap()) 157 | .partial_cmp(queries_hash.get(&rhs.query).unwrap()).unwrap()), 158 | 159 | QueriesSortType::CountInverse => 160 | new_qq.sort_by(|lhs, rhs| 161 | (*queries_hash.get(&rhs.query).unwrap()) 162 | .partial_cmp(queries_hash.get(&lhs.query).unwrap()).unwrap()), 163 | 164 | _ => {} 165 | } 166 | } 167 | 168 | if web { 169 | let mut web_queries = wqq.lock().unwrap(); 170 | 171 | web_queries.clear(); 172 | web_queries.append(&mut new_qq); 173 | } else { 174 | for (index, q) in new_qq.iter().enumerate() { 175 | let count = queries_hash.get(&q.query).unwrap(); 176 | 177 | if *count >= cnf.count_min && *count <= cnf.count_max { 178 | println!("{}", q.to_string(index + 1, *count)); 179 | } 180 | 181 | if index == cnf.limit { 182 | break; 183 | } 184 | } 185 | 186 | println!("TOTAL: {}", qq.len()); 187 | 188 | let filtered = (if new_qq.len() < cnf.limit { 0 } else { qq.len() - new_qq.len() }) + 189 | (if cnf.limit < new_qq.len() && (new_qq.len() - cnf.limit) > 0 { new_qq.len() - cnf.limit - 1 } else { 0 }) + mapflt; 190 | 191 | if filtered > 0 { 192 | println!("FILTERED: {}", filtered.to_string()); 193 | } 194 | } 195 | 196 | qq.clear(); 197 | new_qq.clear(); 198 | } 199 | -------------------------------------------------------------------------------- /src/regs.rs: -------------------------------------------------------------------------------- 1 | use regex::{Captures, Regex}; 2 | 3 | pub fn is_info(line: &String) -> bool { 4 | lazy_static! { 5 | static ref regex: Regex = Regex::new(r"^# .*$").unwrap(); 6 | } 7 | 8 | regex.is_match(line) 9 | } 10 | 11 | pub fn schema(line: &String) -> Option { 12 | lazy_static! { 13 | static ref regex: Regex = Regex::new(r"^# .*Schema: (?P[^\s]+).*$").unwrap(); 14 | } 15 | 16 | regex.captures(line) 17 | } 18 | 19 | pub fn db(line: &String) -> Option { 20 | lazy_static! { 21 | static ref regex: Regex = Regex::new(r"^use (?P.+);$").unwrap(); 22 | } 23 | 24 | regex.captures(line) 25 | } 26 | 27 | pub fn date_time(line: &String) -> Option { 28 | lazy_static! { 29 | static ref regex: Regex = Regex::new(r"^# .*Time: (?P\d{2})(?P\d{2})(?P\d{2})[^\d]+(?P\d+).(?P\d{2}).(?P\d{2}).*$").unwrap(); 30 | } 31 | 32 | regex.captures(line) 33 | } 34 | 35 | pub fn query_time(line: &String) -> Option { 36 | lazy_static! { 37 | static ref regex: Regex = Regex::new(r"^# .*Query_time: (?P\d*\.\d+).*$").unwrap(); 38 | } 39 | 40 | regex.captures(line) 41 | } 42 | 43 | pub fn lock_time(line: &String) -> Option { 44 | lazy_static! { 45 | static ref regex: Regex = Regex::new(r"# .*Lock_time: (?P\d*\.\d+).*$").unwrap(); 46 | } 47 | 48 | regex.captures(line) 49 | } 50 | 51 | pub fn rows_sent(line: &String) -> Option { 52 | lazy_static! { 53 | static ref regex: Regex = Regex::new(r"^# .*Rows_sent: (?P\d+).*$").unwrap(); 54 | } 55 | 56 | regex.captures(line) 57 | } 58 | 59 | pub fn rows_examined(line: &String) -> Option { 60 | lazy_static! { 61 | static ref regex: Regex = Regex::new(r"^# .*Rows_examined: (?P\d+).*$").unwrap(); 62 | } 63 | 64 | regex.captures(line) 65 | } 66 | 67 | pub fn rows_affected(line: &String) -> Option { 68 | lazy_static! { 69 | static ref regex: Regex = Regex::new(r"^# .*Rows_affected: (?P\d+).*$").unwrap(); 70 | } 71 | 72 | regex.captures(line) 73 | } 74 | 75 | pub fn timestamp(line: &String) -> Option { 76 | lazy_static! { 77 | static ref regex: Regex = Regex::new(r"SET timestamp=(?P\d+);$").unwrap(); 78 | } 79 | 80 | regex.captures(line) 81 | } 82 | 83 | pub fn addr_port(line: &String) -> Option { 84 | lazy_static! { 85 | static ref regex: Regex = Regex::new(r"(?P\d+\.\d+\.\d+\.\d+):(?P\d+)").unwrap(); 86 | } 87 | 88 | regex.captures(line) 89 | } 90 | 91 | pub fn is_query_end(line: &String) -> bool { 92 | lazy_static! { 93 | static ref regex: Regex = Regex::new(r"^.*;$").unwrap(); 94 | } 95 | 96 | !regex.find(line).is_none() 97 | } 98 | 99 | pub fn abs_numbers(line: &String) -> String { 100 | lazy_static! { 101 | static ref regex1: Regex = Regex::new(r"\s*=\s*(?P\d+)\s*").unwrap(); 102 | } 103 | 104 | lazy_static! { 105 | static ref regex2: Regex = Regex::new(r"\s\d+").unwrap(); 106 | } 107 | 108 | let ret: String = regex1.replace_all(line, " = |NUMBER| ").into(); 109 | 110 | regex2.replace_all(&ret, " |NUMBER| ").into() 111 | } 112 | 113 | pub fn abs_strings(line: &String) -> String { 114 | lazy_static! { 115 | static ref regex: Regex = Regex::new(r"(?P'.*?')").unwrap(); 116 | } 117 | 118 | regex.replace_all(line, " |STRING| ").into() 119 | } 120 | 121 | pub fn prs_spaces_trim(line: &String) -> String { 122 | lazy_static! { 123 | static ref regex1: Regex = Regex::new(r"\(\s+").unwrap(); 124 | } 125 | 126 | lazy_static! { 127 | static ref regex2: Regex = Regex::new(r"\s+\)").unwrap(); 128 | } 129 | 130 | let ret: String = regex1.replace_all(line, "(").into(); 131 | 132 | regex2.replace_all(&ret, ")").into() 133 | } 134 | 135 | pub fn remove_comments(line: &String) -> String { 136 | lazy_static! { 137 | static ref aster_regex: Regex = Regex::new(r"/\*[^!].+?\*/").unwrap(); 138 | } 139 | 140 | lazy_static! { 141 | static ref dash_regex: Regex = Regex::new(r"-- .*").unwrap(); 142 | } 143 | 144 | lazy_static! { 145 | static ref hash_regex: Regex = Regex::new(r"# .*").unwrap(); 146 | } 147 | 148 | let aster_ret: String = aster_regex.replace_all(line, "").into(); 149 | let dash_ret: String = dash_regex.replace_all(&aster_ret, "").into(); 150 | 151 | hash_regex.replace_all(&dash_ret, "").into() 152 | } 153 | -------------------------------------------------------------------------------- /src/types.rs: -------------------------------------------------------------------------------- 1 | use chrono::TimeZone; 2 | use chrono::prelude::Utc; 3 | use std::sync::Mutex; 4 | use regex::Regex; 5 | use std::time::Duration; 6 | 7 | struct QueryStore { 8 | pub db: String, 9 | pub timestamp: i64 10 | } 11 | 12 | impl QueryStore { 13 | pub fn new() -> Self { 14 | Self { 15 | db: "?".to_string(), 16 | timestamp: -1 17 | } 18 | } 19 | } 20 | 21 | lazy_static! { 22 | static ref store: Mutex = Mutex::new(QueryStore::new()); 23 | } 24 | 25 | pub enum QueriesSortType { 26 | Timestamp = 0, 27 | QueryTime, 28 | LockTime, 29 | RowsSent, 30 | RowsExamined, 31 | RowsAffected, 32 | TimestampInverse, 33 | QueryTimeInverse, 34 | LockTimeInverse, 35 | RowsSentInverse, 36 | RowsExaminedInverse, 37 | RowsAffectedInverse, 38 | Count, 39 | CountInverse, 40 | Undefined 41 | } 42 | 43 | impl ToString for QueriesSortType { 44 | fn to_string(&self) -> String { 45 | match self { 46 | &QueriesSortType::Timestamp => "Timestamp".to_string(), 47 | &QueriesSortType::QueryTime => "Query time".to_string(), 48 | &QueriesSortType::LockTime => "Lock time".to_string(), 49 | &QueriesSortType::RowsSent => "Rows sent".to_string(), 50 | &QueriesSortType::RowsExamined => "Rows examined".to_string(), 51 | &QueriesSortType::RowsAffected => "Rows affected".to_string(), 52 | &QueriesSortType::TimestampInverse => "Timestamp inverse".to_string(), 53 | &QueriesSortType::QueryTimeInverse => "Query time inverse".to_string(), 54 | &QueriesSortType::LockTimeInverse => "Lock time inverse".to_string(), 55 | &QueriesSortType::RowsSentInverse => "Rows sent inverse".to_string(), 56 | &QueriesSortType::RowsExaminedInverse => "Rows examined inverse".to_string(), 57 | &QueriesSortType::RowsAffectedInverse => "Rows affected inverse".to_string(), 58 | &QueriesSortType::Count => "Count".to_string(), 59 | &QueriesSortType::CountInverse => "Count inverse".to_string(), 60 | &QueriesSortType::Undefined => "Undefined".to_string() 61 | } 62 | } 63 | } 64 | 65 | pub struct Config { 66 | pub log_file: String, 67 | pub timestamp_begin: i64, 68 | pub timestamp_end: i64, 69 | pub db: String, 70 | pub query_time_min: f64, 71 | pub query_time_max: f64, 72 | pub lock_time_min: f64, 73 | pub lock_time_max: f64, 74 | pub rows_sent_min: i64, 75 | pub rows_sent_max: i64, 76 | pub rows_examined_min: i64, 77 | pub rows_examined_max: i64, 78 | pub rows_affected_min: i64, 79 | pub rows_affected_max: i64, 80 | pub sort_type: QueriesSortType, 81 | pub regex: Option, 82 | pub count_min: usize, 83 | pub count_max: usize, 84 | pub limit: usize, 85 | pub abs: bool, 86 | pub web_addr: String, 87 | pub web_port: u16, 88 | pub dedup: bool, 89 | pub wpd: Duration, 90 | errors: Vec<&'static str> 91 | } 92 | 93 | impl Config { 94 | pub fn new() -> Self { 95 | Self { 96 | log_file: "mysql-slow.log".to_string(), 97 | timestamp_begin: -1, 98 | timestamp_end: -1, 99 | db: "".to_string(), 100 | query_time_min: -1.0, 101 | query_time_max: -1.0, 102 | lock_time_min: -1.0, 103 | lock_time_max: -1.0, 104 | rows_sent_min: -1, 105 | rows_sent_max: -1, 106 | rows_examined_min: -1, 107 | rows_examined_max: -1, 108 | rows_affected_min: -1, 109 | rows_affected_max: -1, 110 | sort_type: QueriesSortType::Undefined, 111 | regex: None, 112 | count_min: 0, 113 | count_max: 0, 114 | limit: 0, 115 | abs: false, 116 | web_addr: String::new(), 117 | web_port: 0, 118 | dedup: false, 119 | wpd: Duration::from_millis(1), 120 | errors: Vec::new() 121 | } 122 | } 123 | 124 | pub fn add_error(&mut self, err: &'static str) { 125 | let err_index = self.errors.len(); 126 | self.errors.insert(err_index, err); 127 | } 128 | 129 | pub fn has_errors(&self) -> bool { 130 | self.errors.len() != 0 131 | } 132 | 133 | pub fn errors(&self) -> String { 134 | let mut errors_string = "\t".to_string(); 135 | 136 | errors_string.push_str(&self.errors.join("\n\t")); 137 | errors_string.push_str("\n\nYou can use -p flag to print configuration"); 138 | errors_string 139 | } 140 | } 141 | 142 | impl ToString for Config { 143 | fn to_string(&self) -> String { 144 | format!("CONFIGURATION: 145 | \tLog file: \"{}\" 146 | \tDatabase: \"{}\" 147 | \tTimestamp range: {} - {} 148 | \tQuery time range: {} - {} 149 | \tLock time range: {} - {} 150 | \tRows sent range: {} - {} 151 | \tRows examined range: {} - {} 152 | \tRows affected range: {} - {} 153 | \tSort type: {} 154 | \tQuery regex: {:?} 155 | \tCount range: {} - {} 156 | \tLimit: first {} 157 | \tStr & num abstract: {} 158 | \tWeb address: \"{}\" 159 | \tWeb port: {} 160 | \tDeduplication: {}", 161 | self.log_file, 162 | self.db, 163 | self.timestamp_begin, self.timestamp_end, 164 | self.query_time_min, self.query_time_max, 165 | self.lock_time_min, self.lock_time_max, 166 | self.rows_sent_min, self.rows_sent_max, 167 | self.rows_examined_min, self.rows_examined_max, 168 | self.rows_affected_min, self.rows_affected_max, 169 | self.sort_type.to_string(), 170 | self.regex, 171 | self.count_min, self.count_max, 172 | if self.limit < super::std::usize::MAX { self.limit + 1 } else { self.limit }, 173 | self.abs, 174 | self.web_addr, 175 | self.web_port, 176 | self.dedup) 177 | } 178 | } 179 | 180 | #[derive(Clone)] 181 | pub struct Query { 182 | pub timestamp: i64, 183 | pub db: String, 184 | pub query_time: f64, 185 | pub lock_time: f64, 186 | pub rows_sent: i64, 187 | pub rows_examined: i64, 188 | pub rows_affected: i64, 189 | pub query: String, 190 | pub consuming_query: bool, 191 | pub query_consumed: bool 192 | } 193 | 194 | impl Query { 195 | pub const DT_FORMAT: &'static str = "%d/%m/%Y:%H:%M:%S"; 196 | 197 | pub fn new() -> Self { 198 | Self { 199 | timestamp: -1, 200 | db: "?".to_string(), 201 | query_time: -1.0, 202 | lock_time: -1.0, 203 | rows_sent: -1, 204 | rows_examined: -1, 205 | rows_affected: -1, 206 | query: "?".to_string(), 207 | consuming_query: false, 208 | query_consumed: false 209 | } 210 | } 211 | 212 | pub fn valid(&self) -> bool { 213 | self.query_consumed && self.query != "?" 214 | } 215 | 216 | pub fn finish(&mut self) { 217 | let mut st = store.lock().unwrap(); 218 | 219 | if self.db == "?" { 220 | self.db = st.db.clone(); 221 | } else { 222 | st.db = self.db.clone(); 223 | } 224 | 225 | if self.timestamp < 0 { 226 | self.timestamp = st.timestamp; 227 | } else { 228 | st.timestamp = self.timestamp; 229 | } 230 | 231 | self.query = self.query 232 | .replace("\r", " ") 233 | .replace("\n", " ") 234 | .replace("\t", " ") 235 | .trim().to_string(); 236 | 237 | while self.query.contains(" ") { 238 | self.query = self.query.replace(" ", " "); 239 | } 240 | 241 | while self.query.contains(",|STRING|") { 242 | self.query = self.query.replace(",|STRING|", ", |STRING|"); 243 | } 244 | } 245 | 246 | pub fn to_string(&self, index: usize, count: usize) -> String { 247 | let mut buf = format!("> #{} | DATE_TIME: ", index.to_string()); 248 | 249 | if self.timestamp >= 0 { 250 | let date_time = Utc.timestamp(self.timestamp, 0); 251 | buf.push_str(&date_time.format(Self::DT_FORMAT).to_string()); 252 | } else { 253 | buf.push_str("?"); 254 | } 255 | 256 | buf.push_str(&format!(" | DATABASE: {}\n>> QUERY_TIME: ", &self.db)); 257 | 258 | if self.query_time >= 0.0 { 259 | buf.push_str(&self.query_time.to_string()); 260 | } else { 261 | buf.push_str("?"); 262 | } 263 | 264 | buf.push_str(" | ROWS_EXAMINED: "); 265 | 266 | if self.rows_examined >= 0 { 267 | buf.push_str(&self.rows_examined.to_string()); 268 | } else { 269 | buf.push_str("?"); 270 | } 271 | 272 | buf.push_str(" | ROWS_AFFECTED: "); 273 | 274 | if self.rows_affected >= 0 { 275 | buf.push_str(&self.rows_affected.to_string()); 276 | } else { 277 | buf.push_str("?"); 278 | } 279 | 280 | buf.push_str("\n>>> ROWS_SENT: "); 281 | 282 | if self.rows_sent >= 0 { 283 | buf.push_str(&self.rows_sent.to_string()); 284 | } else { 285 | buf.push_str("?"); 286 | } 287 | 288 | buf.push_str(" | LOCK_TIME: "); 289 | 290 | if self.lock_time >= 0.0 { 291 | buf.push_str(&self.lock_time.to_string()); 292 | } else { 293 | buf.push_str("?"); 294 | } 295 | 296 | buf.push_str(&format!(" | COUNT: {}", count.to_string())); 297 | buf.push_str(&format!("\n{}\n", self.query)); 298 | 299 | buf 300 | } 301 | } 302 | -------------------------------------------------------------------------------- /src/web.rs: -------------------------------------------------------------------------------- 1 | use super::{read_queries, config, queries}; 2 | 3 | use types::{Query}; 4 | use std::sync::Mutex; 5 | use std::thread::{self, sleep}; 6 | use processing::{self, qhash}; 7 | use rocket; 8 | use rocket::config::{Config, Environment}; 9 | use chrono::Utc; 10 | 11 | lazy_static! { 12 | pub static ref wqq: Mutex> = Mutex::new(Vec::new()); 13 | } 14 | 15 | lazy_static! { 16 | static ref old_response: Mutex = Mutex::new("Please refresh page later".to_string()); 17 | } 18 | 19 | #[get("/")] 20 | fn all() -> String { 21 | let mut response: Vec = Vec::new(); 22 | let web_queries = wqq.lock().unwrap(); 23 | 24 | let queries_hash = if let Ok(qh_lock) = qhash.try_lock() { 25 | qh_lock 26 | } else { 27 | println!("[{}] WEB: Previous result provided (unable to lock queries count)", Utc::now().to_string()); 28 | return old_response.lock().unwrap().clone(); 29 | }; 30 | 31 | let (count_min, count_max) = { 32 | let cnf = config.lock().unwrap(); 33 | (cnf.count_min, cnf.count_max) 34 | }; 35 | 36 | for (index, query) in web_queries.iter().enumerate() { 37 | let count = { 38 | if let Some(qcount) = queries_hash.get(&query.query) { 39 | *qcount 40 | } else { 41 | 1 42 | } 43 | }; 44 | 45 | if count >= count_min && count <= count_max { 46 | response.push(query.to_string(index + 1, count)); 47 | } 48 | } 49 | 50 | let mut res = old_response.lock().unwrap(); 51 | 52 | *res = response.join("\n"); 53 | res.clone() 54 | } 55 | 56 | pub fn invoke_web() { 57 | let update_thread = thread::spawn(move || { 58 | let wdelay = config.lock().unwrap().wpd; 59 | 60 | loop { 61 | read_queries(true); 62 | let mut qq = queries.lock().unwrap(); 63 | processing::process(&mut qq, true); 64 | sleep(wdelay * 10); 65 | } 66 | }); 67 | 68 | let rocket_config = { 69 | let cnf = config.lock().unwrap(); 70 | 71 | println!("\nWeb server running on {}:{}", cnf.web_addr.clone(), cnf.web_port); 72 | 73 | Config::build(Environment::Production) 74 | .address(cnf.web_addr.clone()) 75 | .port(cnf.web_port) 76 | .workers(10) 77 | .finalize().unwrap() 78 | }; 79 | 80 | rocket::custom(rocket_config, false) 81 | .mount("/", routes![all]) 82 | .launch(); 83 | 84 | update_thread.join().expect_err("Can't succesfully end updating thread"); 85 | } 86 | --------------------------------------------------------------------------------